- vue3封装弹窗组件,数据回显
- 封装组件的流程,第一理解具体的业务需求。想好数据的交互层级,数据进数据出的具体逻辑。
- vue的组件封装记住一点不要在子组件修改父组件的数据,需要修改数据emit方法抛出数据父组件修改。
. 第一步构建template模板
<template>
<a-modal
width="800.0px"
v-model:visible="visible"
@ok="handleOk"
@cancel="handleCancel"
:maskClosable="false"
>
<template #title> {{ title }} </template>
<a-form ref="formRef" :model="state.form" :style="{ width: '600px' }">
<a-form-item
field="dictName"
:rules="[{ required: true, message: '字典名称必填' }]"
:validate-trigger="['change', 'input']"
tooltip="输入字典名称"
label="字典名称"
>
<a-input v-model="state.form.dictName" placeholder="输入字典名称" />
</a-form-item>
<a-form-item
field="dictType"
:rules="[{ required: true, message: '字典类型必填' }]"
label="字典类型"
>
<a-input v-model="state.form.dictType" placeholder="输入字典类型" />
</a-form-item>
<a-form-item
field="status"
:rules="[{ required: true, message: '请选择状态' }]"
label="状态"
>
<a-radio-group v-model="state.form.status">
<a-radio value="0">正常</a-radio>
<a-radio value="1">停用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item field="remark">
<a-textarea
v-model="state.form.remark"
placeholder="请输入内容"
allow-clear
/>
</a-form-item>
</a-form>
</a-modal>
</template>
这就是弹窗组件的一个基本dom结构。这个模板上看的话我们需要父组件传给我们两个值, 一个是弹窗的状态,一个是form表单的值。
. 第二步定义参数属性
const props = defineProps({
visible: {
type: Boolean,
default: () => false,
},
formData: {
type: Object as PropType<Dict.DictType>,
default: () => {
return {
remark: '',
dictId: 0,
dictName: '',
dictType: '',
status: '0',
};
},
},
});
. 第三步就是子组件的赋值
const formRef = ref<FormInstance>();
const state = reactive<Dict.State>({
form: {
remark: '',
dictId: 0,
dictName: '',
dictType: '',
status: '0',
},
});
const { visible, formData } = toRefs(props);
watch(
() => formData,
(value) => {
state.form = value as unknown as Dict.DictType;
},
{
immediate: true,
deep: true,
}
);
接受父组件的formData需要使用toRefs结构不然传过来的值会丢失响应式。
最后把我们验证之后的formData值传递给父组件去调用接口就完成了,在这之前需要声明emit
const emit = defineEmits(['handleClose', 'handleSubmitData']);
const handleCancel = () => {
emit('handleClose', false);
formRef.value?.clearValidate();
};
const handleOk = () => {
formRef?.value?.validate((r: any, Record: any) => {
// eslint-disable-next-line no-void
if (r == void 0) {
emit('handleSubmitData', state.form);
emit('handleClose', false);
}
});
};
- 通过emit的方法把子组件的数据传到父组件。
antv G6
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
</head>
<style>
/* 提示框的样式 */
.g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
</style>
<body>
<!-- 引入 G6 -->
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
<!-- 4.x and later versions -->
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.js"></script>
<div id="mountNode"></div>
<button onclick="getNodes()">getNodes节点</button>
<script>
G6.registerNode('diamond', {
draw(cfg, group) {
// 如果 cfg 中定义了 style 需要同这里的属性进行融合
const keyShape = group.addShape('path', {
attrs: {
path: this.getPath(cfg), // 根据配置获取路径
stroke: cfg.color, // 颜色应用到描边上,如果应用到填充,则使用 fill: cfg.color
},
// must be assigned in G6 3.3 and later versions. it can be any value you want
name: 'path-shape',
// 设置 draggable 以允许响应鼠标的图拽事件
draggable: true,
});
if (cfg.label) {
// 如果有文本
// 如果需要复杂的文本配置项,可以通过 labeCfg 传入
// const style = (cfg.labelCfg && cfg.labelCfg.style) || {};
// style.text = cfg.label;
const label = group.addShape('text', {
// attrs: style
attrs: {
x: 0, // 居中
y: 0,
textAlign: 'center',
textBaseline: 'middle',
text: cfg.label,
fill: '#666',
},
// must be assigned in G6 3.3 and later versions. it can be any value you want
name: 'text-shape',
// 设置 draggable 以允许响应鼠标的图拽事件
draggable: true,
});
}
return keyShape;
},
// 返回菱形的路径
getPath(cfg) {
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
const width = size[0];
const height = size[1];
// / 1 \
// 4 2
// \ 3 /
const path = [
['M', 0, 0 - height / 2], // 上部顶点
['L', width / 2, 0], // 右侧顶点
['L', 0, height / 2], // 下部顶点
['L', -width / 2, 0], // 左侧顶点
['Z'], // 封闭
];
return path;
},
update(cfg, node) {
debugger
console.log(cfg, node);
const group = node.getContainer(); // 获取容器
const shape = group.get('children')[0]; // 按照添加的顺序
const style = {
path: this.getPath(cfg),
stroke: cfg.color,
};
shape.attr(style); // 更新属性
// 更新文本的逻辑类似,但是需要考虑 cfg.label 是否存在的问题
// 通过 label.attr() 更新文本属性即可
},
});
G6.registerNode('inner-animate', {
afterDraw(cfg, group) {
debugger
const size = cfg.size;
const width = size[0] - 14;
const height = size[1] - 14;
// 添加图片
const image = group.addShape('image', {
attrs: {
x: - width / 2,
y: - height / 2,
width: width,
height: height,
img: cfg.img
},
// must be assigned in G6 3.3 and later versions. it can be any value you want
name: 'image-shape'
});
}
},
'rect');
// 实例化 grid 插件
const grid = new G6.Grid();
const graph = new G6.Graph({
container: 'mountNode', // 指定挂载容器
width: 1300, // 图的宽度
height: 1000, // 图的高度
renderer: 'svg',
defaultNode: {
position: 'left',
style: {
background: {
fill: '#ffffff',
stroke: 'green',
padding: [3, 2, 3, 2],
radius: 2,
lineWidth: 3,
},
},
},
defaultEdge: {
autoRotate: true,
style: {
background: {
fill: '#ffffff',
stroke: '#000000',
padding: [2, 2, 2, 2],
radius: 2,
},
},
},
nodeStateStyles: {
// 各状态下的样式,平铺的配置项仅在 keyShape 上生效。需要在其他 shape 样式上响应状态变化则写法不同,参见上文提到的 配置状态样式 链接
hover: {
fillOpacity: 0.1,
lineWidth: 10,
},
},
plugins: [grid], // 将 grid 实例配置到图上
// fitView: true,
// fitViewPadding: [20, 40, 50, 20],
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'
, {
type: 'tooltip', // 提示框
formatText(model) {
// 提示框文本内容
const text = 'label: ' + model.label + '<br/> class: ' + model.class;
return text;
},
},
], // 允许拖拽画布、放缩画布、拖拽节点
},
layout: {
type: 'dagre',
// 布局的方向。T:top(上);B:bottom(下);L:left(左);R:right(右)。
rankdir: 'TB', // 可选,默认为图的中心
// align: 'DL', // 可选
nodesep: 20, // 可选
ranksep: 50, // 可选
controlPoints: true, // 可选
},
// layout: {
// // Object,可选,布局的方法及其配置项,默认为 random 布局。
// type: 'force', // 指定为力导向布局
// preventOverlap: true, // 防止节点重叠
// // nodeSize: 30 // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
// linkDistance: 300, // 指定边距离为100
// },
});
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
const data = {
nodes: [
{
id: 'lusifer',
x: 100,
y: 100,
class: 'self_class',
label: 'rectvvva',
type: 'rect',
style: {
// 仅在 keyShape 上生效
fill: 'lightblue',
stroke: 'green',
lineWidth: 1,
radius: 1,
},
linkPoints: {
top: true,
bottom: true,
left: true,
right: true,
// ... 四个圆的样式可以在这里指定
},
// labelCfg: {...} // 标签的样式可以在这里指定
},
],
};
remoteData.edges.forEach(element => {
// element.type = 'cubic-vertical';
element.type = 'polyline';
element.style = {
endArrow: true,
startArrow: true
}
});
const datav = {
nodes: [
{ id: 'node1', x: 50, y: 100, color: 'red', type: 'diamond' }, // 最简单的
{ id: 'node2', x: 150, y: 100, color: 'red', type: 'diamond', size: [50, 100] }, // 添加宽高
{
id: 'node3', x: 250, y: 100, color: 'red', type: 'inner-animate', size: [100, 100],
img: `./assets/img/info_1.png`
}, // 添加颜色
{ id: 'node4', x: 350, y: 100, color: 'red', label: '菱形', type: 'diamond' }, // 附加文本
],
};
graph.data(remoteData); // 加载远程数据
graph.render(); // 渲染
};
main();
// 监听鼠标进入节点事件
graph.on('node:mouseenter', (evt) => {
const node = evt.item;
// 激活该节点的 hover 状态
graph.setItemState(node, 'hover', true);
});
// 监听鼠标离开节点事件
graph.on('node:mouseleave', (evt) => {
const node = evt.item;
// 关闭该节点的 hover 状态
graph.setItemState(node, 'hover', false);
});
/**
* 获取所有节点
*/
function getNodes( ){
let ns= graph.getNodes();
debugger
console.log(ns);
}
// graph.update(item, cfg)
</script>
</body>
</html>