页面基础布局
在设计高度交互性和自定义性的可视化仪表盘时,页面通常被分为以下三个部分:
- 左侧部分 — 组件选择区:在这里可以选取组件并拖拽到画布上进行展示;
- 中间部分 — 仪表盘编辑展示区:通过拖拽左侧的组件,可以在这里对展示区域的组件进行拖拽、缩放、布局等操作,生成预期的布局效果;
- 右侧部分 — 组件属性编辑区:当选中某一个组件后,可以通过预先开放的接口对组件的预置属性进行编辑操作。
每一个部分各司其职,共同构建用户最终所需的界面。这样的布局不仅提高工作的流畅性,也极大地提升了用户体验。
使用的第三方库
| 库名 | 用途 |
|---|---|
@antv/g2 | 图表渲染 |
grid-layout-plus | 拖拽、缩放布局(基于 Vue3) |
页面具体实现
1. 创建数字图组件(BigNumber)
(1) 创建 BigNumber 文件夹,并在文件夹下创建 index.vue,实现一个数字图组件;
(2) 在 settingConfig.js 中编写数字图组件对应的配置文件,开放字体大小以及字体权重的编辑入口。
components/
BigNumber/
index.vue # 组件实现
settingConfig.js # 可编辑属性配置
2. 实现左侧组件列表展示
(1) 首先在 utils 文件夹下创建 const.js,用来定义组件库的组件列表:
// utils/const.js
export const COMPONENT_LIST = [
{ type: 'BigNumber', name: '数字图', icon: 'NumberOutlined' },
// ... 其他组件
]
(2) 在 LeftComponent.vue 组件中引入并渲染列表,对组件设置 draggable 属性开启拖拽功能,并绑定 data-type 以及 data-name 属性,以便确认用户拖拽的组件类型:
<template>
<div class="left-panel">
<div
v-for="item in componentList"
:key="item.type"
draggable="true"
:data-type="item.type"
:data-name="item.name"
@dragstart="handleDragStart"
>
{{ item.name }}
</div>
</div>
</template>
循环获取页面可拖拽的节点,添加拖拽事件监听,并通过 dataTransfer.setData() 存储当前拖拽组件的类型以及 config 配置:
function handleDragStart(event) {
const type = event.target.dataset.type
const config = getDefaultConfig(type)
event.dataTransfer.setData('type', type)
event.dataTransfer.setData('config', JSON.stringify(config))
}
3. 实现中间区域组件的拖拽缩放渲染功能
(1)获取拖拽的组件信息
在 RenderComponent.vue 组件中给根组件绑定 drop 事件,通过 dataTransfer.getData() 获取刚才存储的拖拽组件信息,并添加到画布的组件列表中:
function handleDrop(event) {
const type = event.dataTransfer.getData('type')
const config = JSON.parse(event.dataTransfer.getData('config'))
// 计算放置位置并添加到布局列表
layoutList.value.push({
i: Date.now().toString(),
x: 0, y: 0, w: 4, h: 3,
type,
config
})
}
(2)实现可拖拽、缩放的布局功能
借助 Grid Layout Plus 插件实现(Grid Layout Plus 是一个基于 Vue3 的布局插件,支持组件的缩放、拖拽等功能,更多用法参考 官方文档):
<template>
<GridLayout
v-model:layout="layoutList"
:col-num="24"
:row-height="30"
:is-draggable="true"
:is-resizable="true"
@drop="handleDrop"
>
<GridItem
v-for="item in layoutList"
:key="item.i"
:i="item.i"
:x="item.x" :y="item.y"
:w="item.w" :h="item.h"
@click="handleSelectItem(item)"
>
<RenderCard :type="item.type" :config="item.config" />
</GridItem>
</GridLayout>
</template>
由于组件库的组件会有多个,可以创建一个 RenderCard.vue 组件,统一引入组件库的所有组件,通过传递组件的 type 属性进行组件的展示:
<!-- RenderCard.vue -->
<template>
<component :is="componentMap[type]" v-bind="config" />
</template>
<script setup>
import BigNumber from '@/components/BigNumber/index.vue'
const componentMap = {
BigNumber,
// ... 其他组件
}
defineProps(['type', 'config'])
</script>
4. 实现右侧区域的组件属性编辑
(1)实现点击事件
点击中间区域的组件,将组件信息传递给 RightComponent.vue 组件,并将 styleFormItem 中的数据动态渲染到 form 表单中:
// 在父组件中
const selectedItem = ref(null)
function handleSelectItem(item) {
selectedItem.value = item
}
<!-- RightComponent.vue -->
<template>
<div v-if="currentItem">
<a-form>
<a-form-item
v-for="field in formItems"
:key="field.key"
:label="field.label"
>
<a-input
v-if="field.type === 'input'"
v-model:value="currentItem.config[field.key]"
/>
<a-input-number
v-else-if="field.type === 'number'"
v-model:value="currentItem.config[field.key]"
/>
</a-form-item>
</a-form>
</div>
</template>
(2)收集表单信息并更新组件渲染
收集表单信息并存储到当前组件的 styleOption 中,并触发组件重新渲染。至此,一个基础的可视化仪表盘设计器就实现了。
// 监听配置变化,实时更新渲染
watch(
() => currentItem.value?.config,
(newConfig) => {
// 触发组件重新渲染
},
{ deep: true }
)
页面效果展示
完成上述开发后,用户可以:
- 从左侧组件区拖拽任意图表组件到中间画布
- 在画布中自由调整组件的位置和大小
- 点击选中任意组件后,在右侧面板编辑字体大小、颜色等属性
- 实时预览最终效果,所见即所得
这套仪表盘设计器的架构具有良好的扩展性,后续可以继续添加折线图、柱状图、饼图等更多组件类型,只需在 const.js 中注册组件信息,并在 RenderCard.vue 中引入对应组件即可。