绘制地图(echarts)
下载地图文件
datav.aliyun.com/portal/scho… 首先需要去这里下载地图数据,例如我们下载了浙江省的地图数据放到我们的本地项目中。
地图注册
echarts.registerMap('ZJ', geoJson); // 先注册地图
渲染地图,展示数据
import geoJson from './zhejiang.json'
echarts.registerMap('ZJ', geoJson); // 先注册地图
var option = {
title: {
text: '',
left: 'center'
},
tooltip: {
trigger: 'item'
},
series: [
{
name: '浙江省',
type: 'map',
mapType: 'ZJ', // 注册的地图类型
roam: true, // 允许缩放和平移
label: {
show: true
},
data: [{
name: '杭州市',
value: 3
}] // 显示的数据
}
]
};
var dom = chartDom.value;
myChart = echarts.init(dom);
// 使用配置项显示地图
myChart.setOption(option);
3D饼图、3D环形图(highcharts)
echarts的方式
3D图用echarts实现,官方没有Demo,只在可视化社区的网站里找到了一些例子。www.makeapie.cn/echarts。
对于可视化的业务来说,Demo的意义也是不可忽视的。
事实上,这个Demo虽然能画出一个3D的环形图,但效果并不是特别好,并且echarts版本切换一下,就无法使用了。
使用highcharts去绘制
相比echarts,highcharts官方提供了Demo和源码:
我们只需要调整一下参数,即可绘制一个比较好看的3D环形图了:
const getChartOptions = (data) => ({
chart: {
type: 'pie',
options3d: {
enabled: true,
alpha: 60,
beta: 0,
depth: 40,
viewDistance: 25,
},
},
title: { text: '' },
subtitle: { text: '' },
legend: { enabled: false },
tooltip: {
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderColor: '#ddd',
borderWidth: 1,
borderRadius: 6,
padding: 10,
shadow: true,
formatter: function () {
return `${this.point.name}:${this.point.y},占比: ${(
((this.point.y / this.point.total) * 100) || 0).toFixed(1)}%`;
},
useHTML: true,
style: {
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
color: '#333',
fontWeight: 'bold',
},
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
size: 150,
depth: 40,
borderWidth: 4,
borderColor: '#fff',
shadow: {
color: 'rgba(0, 0, 0, 0.3)',
width: 8,
offsetX: 2,
offsetY: 2,
},
innerSize: '60%', // 甜甜圈内径
dataLabels: {
enabled: true,
distance: 30,
color: '#333',
style: {
textOutline: 'none',
fontSize: '12px',
fontWeight: 'bold',
},
formatter: function () {
return `${this.point.name}`;
},
},
},
},
credits: { enabled: false },
series: [
{
type: 'pie',
data: data, // 使用传入的data
startAngle: 0,
},
],
});
AI时代的工作方式:Highcharts GPT,简单描述一下需求,就可以快速生成代码。
也支持在线编辑代码,直接在线上把页面调整成我们想要的效果:
流程图(bpmn.js)
最终效果:
整体结构绘制
在bpmn.js里,流程图本质上是一个xml格式的数据,并且后端也有相应的解析的库。
import BpmnViewer from 'bpmn-js';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
const viewer = new BpmnViewer({ container: '#bpmn-container' });
// 加载你的 BPMN XML 字符串
await viewer.importXML(bpmnXml);
bpmn有两种模式,一种是只读,一种是编辑:
Viewer:只读模式,仅用于展示流程图,不可编辑Modeler:全功能编辑模式,支持拖拽节点、连线、配置属性
一般的使用流程是这样的:
-
先用编辑模式将流程图的整体结构绘制出来,如上图,有开始、结束、用户任务、网关再加上连线,这几种基础元件。
-
当流程配置完成之后,在只读模式上,编辑每个节点的相关属性:用户任务的节点,通常会配置审批人;网关节点,通常会配置条件。
例如:一个请假申请的流程,一天以内的假期,只需要上级领导审批;超过一天的假期,要求部门领导审批。
一般比较复杂的界面交互,都在配置条件上。为此用AI简单生成了一个条件编辑器:
需要服务端去解析json配置并且取出其中的条件。
课程地图(antv X6)
预览地址:beat-the-buzzer.github.io/antv-x6-dem…
预览效果:
import { Graph, Shape } from '@antv/x6'
import { register } from '@antv/x6-vue-shape'
import VueNode from './VueNode.vue'
let graph = new Graph({
container: document.getElementById('graph-container'),
width: window.innerWidth - 60,
height: window.innerHeight - 70,
panning: true,
interacting: false,
mousewheel: {
enabled: true,
zoomAtMousePosition: true,
modifiers: 'ctrl',
minScale: 0.5,
maxScale: 3,
},
connecting: {
router: 'manhattan',
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
anchor: 'center',
connectionPoint: 'anchor',
allowBlank: false,
snap: {
radius: 20,
anchor: 'center'
},
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: '#A2B1C3',
strokeWidth: 2,
targetMarker: {
name: 'block',
width: 12,
height: 8,
},
},
},
zIndex: 0,
})
},
validateConnection({ targetMagnet }) {
return !!targetMagnet
},
},
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#5F95FF',
stroke: '#5F95FF',
},
},
},
},
})
register({
shape: 'custom-vue-node',
width: 120,
height: 75,
component: VueNode,
})
建立绘图的公共配置,其中注册custom-vue-node的行为,支持我们用Vue组件去渲染节点,这样可以做到节点高度的定制化。
为了让数据的结构更清晰,这里没有写循环,上方学年学期的节点逐个添加,见文件源码的地址。
课程数据,关键在于position的计算,对数据做遍历,然后计算x、y的值,最后画上连接线。
for (let i = 0; i < res.length; i++) {
let classIndex = genArrIndex(res[i])
if (classIndex !== false) {
classMap[classIndex].push({
...res[i]
})
}
}
console.log('第一步,', classMap, '按照学年学期把数据进行分组')
let classCellsMap = {}
for (let i = 0; i < classMap.length; i++) {
for (let j = 0; j < classMap[i].length; j++) {
cellItem = JSON.parse(JSON.stringify(example6))
cellItem.attrs.text.text = classMap[i][j].kcmc;
cellItem.position.x = genArrIndex(classMap[i][j]) * (150 + 150);
cellItem.position.y = 200 + 100 * j;
cellItem.attrs.custom = genColorAttr(classMap[i][j])
cellItem.ports.items[0].id = `${genId(classMap[i][j])}-${'000' + (Number(j) + 1)}-0000-ports`; // 大类 左右分支 分支排序 节点位置
cellItem.ports.items[1].id = `${genId(classMap[i][j])}-${'000' + (Number(j) + 1)}-0001-ports`;
cellItem.ports.items[2].id = `${genId(classMap[i][j])}-${'000' + (Number(j) + 1)}-0002-ports`;
cellItem.ports.items[3].id = `${genId(classMap[i][j])}-${'000' + (Number(j) + 1)}-0003-ports`;
cellItem.id = `${genId(classMap[i][j])}-${'000' + (Number(j) + 1)}-0000-node`
cellItem.i = i
cellItem.j = j
cellItem.origin = classMap[i][j] // 原始数据存一下 或许点击的时候会用到
cells.push(cellItem)
classCellsMap[classMap[i][j].kcmc] = JSON.parse(JSON.stringify(cellItem))
// 如果不是第一个,就画个线
if (j > 0) {
cellItem = JSON.parse(JSON.stringify(example7));
cellItem.source.cell = `000${genArrIndexRoot(classMap[i][j])}-000${genArrIndexLevel2(classMap[i][j])}-000${(Number(j))}-0000-node`;
cellItem.source.port = `000${genArrIndexRoot(classMap[i][j])}-000${genArrIndexLevel2(classMap[i][j])}-000${(Number(j))}-0002-ports`;
cellItem.target.cell = `000${genArrIndexRoot(classMap[i][j])}-000${genArrIndexLevel2(classMap[i][j])}-000${(Number(j) + 1)}-0000-node`;
cellItem.target.port = `000${genArrIndexRoot(classMap[i][j])}-000${genArrIndexLevel2(classMap[i][j])}-000${(Number(j) + 1)}-0000-ports`;
cellItem.id = `0000-0000-${'000' + (Number(i) + 1)}-${'000' + (Number(j) + 1)}`;
cellItem.attrs = { line: { targetMarker: null } }
cells.push(cellItem);
}
}
}
console.table(cells)
如果希望自己配置节点的样式,可以把CSS属性放到服务端维护,传给Vue节点,然后再进行样式自定义的实现。