最近领导要求系统首页布局改一下,类似于自助建站那种首页可以实现模块的自定义拖拽,并可以实现保存等功能,一下让我这种VUE新手有点慌张,上网上搜一下也没有对应的结局方案,那就自己写吧。不过最终磕磕绊绊也算是实现了。废话不多说,先上功能。
首先首页实现功能卡片定义,并可自定义调整大小存储等,卡片内容随意定义
多种布局可选,布局也可以随意定义
功能卡片的增删等功能(基于layout面板上存在卡片展示)
先来说实现拖拽功能的组件vue-grid-layout
官网 vue-grid-layout官网
直接上用法
安装
# install with npm
npm install vue-grid-layout --save
# install with yarn
yarn add vue-grid-layout
两种安装方式 一般都用npm安装吧
安装完之后直接引入即可使用
import VueGridLayout from 'vue-grid-layout';
export default {
components: {
GridLayout: VueGridLayout.GridLayout,
GridItem: VueGridLayout.GridItem
},
}
首先就是定义一个layout
<grid-layout :layout="layout"
:col-num="6"
:row-height="30"
:autoSize="true"
:is-draggable="isdiy"
:is-resizable="isdiy"
:useCssTransforms="true"
:vertical-compact="true"
:margin="[10, 10]"
:use-css-transforms="true"
@layout-updated="layoutUpdatedEvent"
>
><grid-item
v-for="item in layout"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:key="item.i"
@move="moveEvent"
@moved="movedEvent"
class="griditem"
v-bind:class="cardLayOut"
>
先来解释一下这些参数的含义
:col-num="6" 表示网格有多少列 数字
:row-height="30" 表示一行的高度(以像素为单位) 数字
:is-draggable="true" 表示网格项数是否可以拖动 Boolean (我上面定义为isdiy开关是为了实现保存后无法拖动的效果)
:is-resizable="true" 表示网格是否可以改变带大小 Boolean(我上面定义为isdiy开关是为了实现保存后无法改变大小的的效果)
:autoSize="ture" 容器是否适应内部变化 Boolean
:margin="[10, 10]" 网格之间的边距 两个数字组成的数组 第一个数字为水品距离 第二个为垂直距离
:use-css-transforms="true" 是否使用css的transforms来排版 为false时 使用后采用定位方式来布局
@layout-updated="layoutUpdatedEvent" 布局更新事件(如果想存储到后台的话,这个事件还是很有用的,后面讲)
x,y,w,h,i 分别为卡片的横纵宽高和唯一索引标识
@move="moveEvent" 布局中元素移动事件 鼠标移动后触发
@moved="movedEvent" 布局中元素移动事件移动结束触发 只有当位置相对上一次发生改变才会触发
布局的定义,因为需要多种布局,我的代码中均通过用户选择的布局类型进行布局的定义,下面是其中一种布局
this.layout = [{
"x": 0,
"y": 0,
"w": 3,
"h": 12,
"i": "1",
"icon": "el-icon-bell",
"cname": "通知公告",
component: "sysNoticeCard",
},
{
"x": 0,
"y": 12,
"w": 4,
"h": 6,
"i": "2",
"icon": "el-icon-bell",
"cname": "缴费提醒",
component: "feeCard"
},
{
"x": 3,
"y": 0,
"w": 3,
"h": 6,
"i": "3",
"icon": "el-icon-warning",
"cname": "法律状态变化",
component: "patLegalCard"
},
{
"x": 3,
"y": 6,
"w": 3,
"h": 6,
"i": "4",
"icon": "el-icon-refresh",
"cname": "专利转移",
component: "patTraCard"
},
{
"x": 4,
"y": 12,
"w": 2,
"h": 6,
"i": "5",
"icon": "el-icon-edit",
"cname": "提案代办",
component: "propsCard"
}
]
icon为功能卡片绑定图标,cname为每个功能卡片名字,component为功能卡片引入的内容
引入方式很简单,类似上面vue-grid-layout的引入方式
有了layout布局和内容,先来实现布局保存到后台
首先需要记录功能卡片内容的数据库表,这里不再贴出,根据自己卡片定义内容来
保存方法
save() {
var param = JSON.parse(JSON.stringify(this.layout));
var layouttype = JSON.parse(JSON.stringify(this.layouttype));
saveLayout(param).then(r => {
saveLayouttype(layouttype).then(r => {
this.$notify({
title: '保存成功',
message: '保存布局成功',
type: 'success'
});
})
})
this.diyStatus = "close";
this.isdiy = false;
},
因为我这里还需要保存用户所选择的面板类型,所以多了一层保存方法
用户在随意拖拽后会触发上面定义的 layoutUpdatedEvent方法
layoutUpdatedEvent: function(newLayout) {
this.layout = newLayout;
},
我这里将用户拖拽后的layout布局赋给预先定义的布局,实现布局的实时更新
保存到后台后即可对数据进行解析和保存
后台代码很简单,所以这边只贴出部分代码
@Override
@Transactional
public boolean saveLayout(UserVO currentuser, List<LayoutVo> layoutStyle) {
SysLayout sl = new SysLayout();
sl.setId((sysLayoutMapper.selectByUserId(currentuser.getUserId())).getId());
EntityWrapper<SysLayoutStyle> slss=new EntityWrapper<>();
slss.eq("l_id", sl.getId());
sysLayoutStyleMapper.delete(slss);
for (int i = 0; i < layoutStyle.size(); i++) {
LayoutVo lv = layoutStyle.get(i);
SysLayoutStyle sysLayoutStyle = new SysLayoutStyle();
sysLayoutStyle.setLId(sl.getId());
sysLayoutStyle.setCardId(lv.getI());
sysLayoutStyle.setCX(lv.getX());
sysLayoutStyle.setCY(lv.getY());
sysLayoutStyle.setCW(lv.getW());
sysLayoutStyle.setCH(lv.getH());
sysLayoutStyle.setCardName(lv.getComponent());
sysLayoutStyle.setCardicon(lv.getIcon());
sysLayoutStyle.setCName(lv.getCname());
sysLayoutStyle.insert();
}
return true;
}
到此,可以实现了布局的定义、拖拽、保存操作
当用户保存后再次进入系统或页面可以直接读取数据库数据展示在页面上