历史记录组件触发 连带menu组件路由同步
![](https://i-blog.csdnimg.cn/blog_migrate/66cf51ae03a340911a102836a955dbc8.png)
修改菜单 默认激活的路由。
历史记录路由如果当前路由在最后一个位置,删除则自动向前移位。
![](https://i-blog.csdnimg.cn/blog_migrate/0531405b6839e30f40a098eabcac3557.png)
解决第一个小bug,点击tab高亮之后路由不跳
![](https://i-blog.csdnimg.cn/blog_migrate/bd822256b5e7d841a9957ab3571238e0.png)
点击最后一个tab删除,修改了menuid,同步路由跳转
![](https://i-blog.csdnimg.cn/blog_migrate/a554c9b95d4456de82bfd42ad11fc2e1.png)
点击删除将tab删空,让导航跳转到系统首页
![](https://i-blog.csdnimg.cn/blog_migrate/cf7e580b7411429a8d26fafbaba43b68.png)
分析项目路由界面的结构
三部分组成
1.form表单
2.table表格数据
3.数据分页
分装form表单组件
![](https://i-blog.csdnimg.cn/blog_migrate/27afdee9fdde51ffb11a27cebd15d4be.png)
封装组件使用无名和具名插槽可以实现form动态效果
<template>
<div>
<!-- 自定义的form -->
<my-form :model="formInline">
<el-form-item label="用户名">
<el-input v-model="formInline.user" placeholder="审批人"></el-input>
</el-form-item>
<template #footer>
<el-button type="primary" @click="onSubmit">查询</el-button>
<el-button type="success" @click="onSubmit">新增</el-button>
<el-button type="danger" @click="onSubmit">批量删除</el-button>
</template>
</my-form>
</div>
</template>
定义该组件的插槽
<template>
<el-form :inline="inline" :model="model" class="demo-form-inline">
<!-- 无名插槽 -->
<slot></slot>
<!-- 具名插槽 -->
<el-form-item>
<slot name="footer"></slot>
</el-form-item>
</el-form>
</template>
定义form表单格式数据
formInline: [
{
name: "用户名",
ele: "input",
key: "name",
},
{
name: "爱好",
ele: "select",
key: "hobby",
options: [
{ value: "1", label: "篮球" },
{ value: "2", label: "足球" },
{ value: "3", label: "羽毛球" },
],
},
],
根据格式进行动态渲染
<el-form :inline="inline" :model="formItem" class="demo-form-inline">
<!-- 无名插槽 -->
<template v-for="(item, index) in model">
<el-form-item :label="item.name">
<template v-if="item.ele == 'input'">
<el-input v-model="formItem[item.key]" :placeholder="item.name" />
</template>
<template v-if="item.ele == 'select'">
<el-select v-model="formItem[item.key]" placeholder="请选择">
<el-option
v-for="items in item.options"
:key="items.value"
:label="items.label"
:value="items.value"
>
</el-option>
</el-select>
</template>
</el-form-item>
</template>
<!-- <slot></slot> -->
<!-- 具名插槽 -->
<el-form-item>
<!-- 局部作用域插槽-->
<slot name="footer" :item="formItem"></slot>
</el-form-item>
</el-form>
根据form表单数据格式进行form数据整理
![](https://i-blog.csdnimg.cn/blog_migrate/18f217f5f7434439195e69cff5cb87d1.png)
封装table表格组件
![](https://i-blog.csdnimg.cn/blog_migrate/04749aace0ee3e1766a158b63bc56b6c.png)
分析复用table,table数据源来自父组件,所以需要使用父子传值方案:props定义属性接收。
props基本使用方案
props:['tableData'],
//根据分析修改props定义方案
props:{
tableData:{
type:Array,
default:[]
}
},
写成上面的default:[] 格式之后 会报错
![](https://i-blog.csdnimg.cn/blog_migrate/e1ee51b7f612a52a00a8a8cb3ccdafc8.png)
分析tablecolumn 中列存在复用,需要将列的标题属性在使用props进行传递
![](https://i-blog.csdnimg.cn/blog_migrate/58ed44259b4f3e20f325dd572da0ca30.png)
![](https://i-blog.csdnimg.cn/blog_migrate/26ea19c3993c0e431dc7db1ef7be2794.png)
在设置table 复选之后,操做复选事件,需要触发当前自定义组件的自定义事件
![](https://i-blog.csdnimg.cn/blog_migrate/6fc8c5eb1c142a7b20155d769981ccd5.png)
触发自定义事件需要在当前组件内部使用$emit 触发
![](https://i-blog.csdnimg.cn/blog_migrate/69d8e0d798852f489b8ff64583ad74bf.png)
table表格数据列渲染数据或者标签
![](https://i-blog.csdnimg.cn/blog_migrate/bd9c7dd98829fba20f00268b3d1e5cbf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6b705edf5a7022416a219ad1525c7155.png)
模拟官方实现
![](https://i-blog.csdnimg.cn/blog_migrate/5af6f0d0bf71ec19ea659a88e605b0e2.png)
在父组件中给封装组件对应位置设置插槽内容
![](https://i-blog.csdnimg.cn/blog_migrate/bf6aeae602708d238845657eb68debf7.png)
以上写法,只是实现td列添加标签
如果在插槽里面需要使用当前table表格数据。(当前行数据)
table表格内部插槽位置不确定,插槽位置不固定。
先调整封装的table的字段(key键值)
父组件中 自定义的table组件 key值调整格式
:tdKey="[{ key: 'date' }, { key: 'name', slot: true }, { key: 'address' }]"
注意在elementui 库table组件中,el-table-column内部不能直接使用插槽。而是调整为谁需要插槽给谁设置插槽。
![](https://i-blog.csdnimg.cn/blog_migrate/4a6b412ed893dabac75920121913ce0a.png)
封装数据分页组件
将自定义的table和分页组件合并
实现最终效果
![](https://i-blog.csdnimg.cn/blog_migrate/c92e58eac255b9297ead161eb5befece.png)
见代码。
mytable组件,分页也写在这个组件中和表格是一体的
<template>
<div class="table">
<el-table
:stripe="stripe"
:border="border"
:data="tableData"
style="width: 100%"
@selection-change="handleSelectionChange"
>
<!-- 在写复选框的时候用到了子传父,方法是在复用组件中写一个方法 在上方绑定 @selection-change,这个是elelmentui中给出的方法触发事件,然后定义一个方法写到下方methods,然后在展示组件中定义一个方法,并且在上方标签中绑定这个方法,起个名字比如 @checkhandler="checkList" 名字叫checkhandler ,然后在复用组件中,通过官方 给定的api this.$emit("checkhandler", value); 这个方式就可以达到效果了,这里双引号的名字记得跟展示页面绑定的方法名字一样 ,至于复选框是否需要隐藏使用props传递来控制的-->
<!-- 复选框 -->
<!-- el-table-column 组件v-show 无效 -->
<el-table-column v-if="ischeck" type="selection" width="55"> </el-table-column>
<!-- 渲染数据列 -->
<!-- 这的v-for后面可以是tdkey也可以是tilte,因为这里的目的只是为了取下标,在页面展示组件中,这两个集合是一样的长度. 所以在这里使用的时候通过属性名[index(下标)的方式就可以直接获取需要的值] -->
<template v-for="(item, index) in tdKey">
<template v-if="!item.slot">
<!-- 不带插槽 -->
<el-table-column
:key="index"
:prop="item.key"
:label="title[index]"
></el-table-column>
</template>
<template v-else>
<el-table-column :key="index" :prop="item.key" :label="title[index]">
<template slot-scope="scope">
<slot :name="item.key" :row="scope.row"> </slot>
</template>
</el-table-column>
</template>
</template>
<!-- 操做插槽 -->
<el-table-column label="操做">
<template slot-scope="scope">
<slot name="handle" :row="scope.row"></slot>
</template>
</el-table-column>
</el-table>
<!--分页组件 -->
<el-pagination
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="pageSizes"
:page-size="pagesize"
:layout="layout"
:total="totle"
>
</el-pagination>
</div>
</template>
<script>
export default {
name: "mytable",
props: {
tdslot: { type: Array, default: () => [] },
ischeck: { type: Boolean, default: false },
border: { type: Boolean, default: false },
stripe: {
type: Boolean,
default: false,
},
title: {
type: Array,
required: true,
default: () => [],
},
tdKey: {
type: Array,
required: true,
default: () => [],
},
tableData: {
type: Array,
default: () => [],
},
// 分页props
pageSizes: { type: Array, default: () => [10, 20, 30, 40] },
layout: {
type: String,
default: "total, sizes, prev, pager, next, ->, jumper",
},
totle: {
type: Number,
default: 0,
},
},
data() {
return {
currentPage: 1, //当前页
pagesize: 10, //一页10条
};
},
methods: {
//复选事件
handleSelectionChange(value) {
console.log("值", value);
//触发该组件的自定义事件
this.$emit("checkhandler", value);
},
// 分页功能
//一页显示多少条变化
handleSizeChange(val) {
this.pagesize = val;
//触发自定义事件
this.$emit("sizechange", val);
},
//当前页变化
handleCurrentChange(val) {
this.currentPage = val;
//触发自定义事件
this.$emit("currentchange", val);
},
},
};
</script>
<style>
</style>
然后渲染在user页面展示组件中
<template>
<div>
<!-- 自定义的form -->
<my-form :model="formInline">
<!-- #footer就相当于v-slot,具名插槽就可以通过#name来使用 ,在后面使用一个变量去接收封装的公共组件中传过来的局部作用域里面的属性中的值 这的footer.item就是数据-->
<template #footer="{ item }">
<el-button @click="onSubmit(item)">查询</el-button>
<el-button type="primary" @click="onSubmit">新增</el-button>
<el-button type="danger" @click="onSubmit">批量删除</el-button>
</template>
</my-form>
<!-- 挂载自定义table 这块传过去的值都是固定的(当然也可以写成变量在data中定义即可),利用的是父传子
复用组件中props中定义的名字是啥,这里的:后面的名字就是啥,然后等于号后面可以接固定的变量,也可以接下方data中传入的动态的值
-->
<my-table
:ischeck="true"
:stripe="true"
:border="true"
:tableData="nowData"
:title="title"
:tdKey="tdKey"
:tdslot="['name']"
@checkhandler="checkList"
:pageSizes="[1, 5, 10, 15]"
:totle="totle"
@sizechange="changesize"
@currentchange="changeCurrent"
>
<!--status插槽 禁用状态-->
<template #status="{ row }">
<el-tag :type="row.status ? '' : 'danger'">{{
row.status ? "正常" : "禁用"
}}</el-tag>
</template>
<!-- 操做插槽 -->
<template #handle="{ row }">
<el-button type="text">修改</el-button>
<el-button type="text">删除</el-button>
</template>
</my-table>
</div>
</template>
<script>
//引入form组件
import formTab from "../../components/elementui/formtab.vue"; //要用到所以引入的,当然如果这个封装用的多了也可以在 全局引入,在main,中引入这个组件,然后,vue下。components用就行
import myTable from "../../components/elementui/mytable.vue";//引入封装的表格内容
export default {
components: {
"my-form": formTab, //给他起个名字,用的时候就直接用名字当做标签名。
"my-table":myTable,
},
data() {
return {
formInline: [
{
name: "用户名",
ele: "input",
key: "name",
},
],
title: ["ID", "用户名", "邮箱", "手机号", "状态", "时间", "操做"],
tdKey: [
{ key: "id" },
{ key: "name" },
{ key: "email" },
{ key: "tel" },
{ key: "status", slot: true },
{ key: "time" },
],
userInfo: [
{
id: "1001",
name: "王小虎",
email: "***@qq.com",
tel: "110",
status: 0,
time: "2016-05-04",
},
{
id: "1002",
name: "王小虎",
email: "***@qq.com",
tel: "110",
status: 0,
time: "2016-05-04",
},
{
id: "1003",
name: "王小虎",
email: "***@qq.com",
tel: "110",
status: 0,
time: "2016-05-04",
},
],
//分页属性
current: 1, //默认当前页
pageSize: 10, //默认一页显示多少条
nowData: [], //模拟当前页数据
totle: 0, //总页数
};
},
created() {
//初始化完成之后获取首次数据
this.findData();
},
methods: {
//获取分页数据的方法
findData() {
// 当前页 多少条
let currentPage = this.current;
let pageSize = this.pageSize;
this.nowData = this.userInfo.slice(
(currentPage - 1) * pageSize,
currentPage * pageSize
);
//赋值总页数
this.totle = this.userInfo.length;
console.log(this.nowData);
},
onSubmit(form) {
console.log("submit!", form);
},
//自定义复选事件执行函数
checkList(value) {
console.log(value);
},
//一页显示多少条变化
changesize(val) {
this.pageSize = val;
},
changeCurrent(val) {
this.current = val;
},
},
watch: {
pageSize() {
this.findData();
},
current() {
this.findData();
},
},
};
</script>
<style></style>