vue使用antv-x6 绘制流程图DAG图(二)

代码:

<template><div class="graph-wrap" @click.stop="hideFn"><Toobar :graph="graph"></Toobar><!-- 小地图 --><div id="minimap" class="mini-map-container"></div><!-- 画布 --><div id="container" /><!-- 右侧节点配置 --><ConfigPanelclass="right-config":nodeData="nodeData":saveType="saveType"></ConfigPanel><!-- 右键 --><Contextmenuv-if="showContextMenu"ref="menuBar"@callBack="contextMenuFn"></Contextmenu></div>
</template><script>
import { Graph, Node, Path, Cell, Addon } from "@antv/x6";
import { register } from "@antv/x6-vue-shape";
import { Dnd } from "@antv/x6-plugin-dnd";
import { MiniMap } from "@antv/x6-plugin-minimap";
import { Scroller } from "@antv/x6-plugin-scroller";
import { Selection } from "@antv/x6-plugin-selection";import ConfigPanel from "./components/configPanel.vue";
import Contextmenu from "./components/contextmenu.vue";
import DataBase from "./components/nodeTheme/dataBase.vue";
import Toobar from "./components/toobar.vue";export default {name: "Graph",props: {// 左侧引擎模版数据stencilData: {type: Object,default: () => {return {};},},graphData: {type: Array,default: () => {return [];},},// 保存类型saveType: {type: String,default: () => {return "strategy";},},},watch: {graphData: {handler(newVal) {// console.log(newVal, 5555);this.nodeStatusList = [];for (let i = 0; i < newVal.length; i++) {if (newVal[i].shape === "dag-node") {if (newVal[i].data.status != null) {this.nodeStatusList.push({id: newVal[i].id,status: newVal[i].data.status,});}}}this.startFn(newVal);},//   deep: true,// immediate: true,},},components: {ConfigPanel,Contextmenu,Toobar,},computed: {isDetail() {if (this.$route.path === "/taskCenter/taskPlan/planDetails") {return true;} else {return false;}},},data() {return {graph: "", // 画布timer: "",showContextMenu: false, // 右键dnd: null, // 左侧nodeData: {}, // 当前节点数据nodeStatusList: [], // 节点状态};},destroyed() {clearTimeout(this.timer);this.timer = null;this.graph.dispose(); // 销毁画布},mounted() {// 初始化 graphthis.initGraph();},methods: {// 隐藏右键hideFn() {this.showContextMenu = false;},// 右键事件contextMenuFn(type, itemData) {switch (type) {case "remove":if (itemData.type === "edge") {this.graph.removeEdge(itemData.item.id);} else if (itemData.type === "node") {this.graph.removeNode(itemData.item.id);}break;}this.showContextMenu = false;},// 注册vue组件节点   2.x 的写法registerCustomVueNode() {register({shape: "dag-node",width: 185,height: 40,component: DataBase,ports: {groups: {top: {position: "top",attrs: {circle: {r: 4,magnet: true,stroke: "#C2C8D5",strokeWidth: 1,fill: "#fff",},},},bottom: {position: "bottom",attrs: {circle: {r: 4,magnet: true,stroke: "#C2C8D5",strokeWidth: 1,fill: "#fff",},},},},},});},// 注册边registerCustomEdge() {Graph.registerEdge("dag-edge",{inherit: "edge",attrs: {line: {stroke: "rgba(0, 0, 0, 0.3)",strokeWidth: 1,targetMarker: {name: "block",width: 12,height: 8,},},},},true);},// 注册连接器registerConnector() {Graph.registerConnector("algo-connector",(s, e) => {const offset = 4;const deltaY = Math.abs(e.y - s.y);const control = Math.floor((deltaY / 3) * 2);const v1 = { x: s.x, y: s.y + offset + control };const v2 = { x: e.x, y: e.y - offset - control };return Path.normalize(`M ${s.x} ${s.y}L ${s.x} ${s.y + offset}C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}L ${e.x} ${e.y}`);},true);},initGraph() {this.registerCustomVueNode();this.registerCustomEdge();this.registerConnector();const graph = new Graph({container: document.getElementById("container"),autoResize: true,// width: 800,// height: 600,background: {color: "rgba(37, 50, 82, 0.1)", // 设置画布背景颜色},grid: {size: 10, // 网格大小 10pxvisible: false, // 渲染网格背景},// 画布平移, 不要同时使用 scroller 和 panning,因为两种形式在交互上有冲突。// panning: {//   enabled: true,//   eventTypes: ["leftMouseDown", "mouseWheel"],// },// 画布缩放mousewheel: {enabled: true,modifiers: "ctrl",factor: 1.1,maxScale: 1.5,minScale: 0.5,},highlighting: {magnetAdsorbed: {name: "stroke",args: {attrs: {fill: "#fff",stroke: "#31d0c6",strokeWidth: 4,},},},},connecting: {snap: true,allowBlank: false,allowLoop: false,highlight: true,connector: "algo-connector",connectionPoint: "anchor",anchor: "center",validateMagnet({ magnet }) {return magnet.getAttribute("port-group") !== "top";},createEdge() {return graph.createEdge({shape: "dag-edge",attrs: {line: {strokeDasharray: "5 5",},},zIndex: -1,});},},// 点击选中 1.x 版本// selecting: {//   enabled: true,//   multiple: true,//   rubberEdge: true,//   rubberNode: true,//   modifiers: "shift",//   rubberband: true,// },});// 点击选中 2.x 版本graph.use(new Selection({multiple: true,rubberEdge: true,rubberNode: true,modifiers: "shift",rubberband: true,}));this.graph = graph;this.initAddon(); // 初始化 拖拽this.graphEvent();this.initScroller();this.initMiniMap();},// 画布事件graphEvent() {const self = this;// 边连接/取消连接this.graph.on("edge:connected", ({ edge }) => {// 目标一端连接桩只允许连接输入if (/out/.test(edge.target.port) || !edge.target.port) {this.$message.error("目标一端连接桩只允许连接输入!");return this.graph.removeEdge(edge.id);}edge.attr({line: {strokeDasharray: "",},});});// 改变节点/边的数据时this.graph.on("node:change:data", ({ node }) => {const edges = this.graph.getIncomingEdges(node);const { status } = node.getData();console.log(status, 77777);edges?.forEach((edge) => {if (status === "running") {edge.attr("line/strokeDasharray", 5);edge.attr("line/style/animation","running-line 30s infinite linear");} else {edge.attr("line/strokeDasharray", "");edge.attr("line/style/animation", "");}});});// 节点右键事件this.graph.on("node:contextmenu", ({ e, x, y, node, view }) => {this.showContextMenu = true;this.$nextTick(() => {this.$refs.menuBar.initFn(e.pageX, e.pageY, {type: "node",item: node,});});});// 边右键事件this.graph.on("edge:contextmenu", ({ e, x, y, edge, view }) => {this.showContextMenu = true;this.$nextTick(() => {this.$refs.menuBar.initFn(e.pageX, e.pageY, {type: "edge",item: edge,});});});// 节点单击事件this.graph.on("node:click", ({ e, x, y, node, view }) => {// console.log(node, 2222);// console.log(node.store.data.data.engine);this.$nextTick(() => {this.nodeData = {id: node.id,store: node.store,};});});// 鼠标抬起this.graph.on("node:mouseup", ({ e, x, y, node, view }) => {// self.$emit("saveGraph");});//平移画布时触发,tx 和 ty 分别是 X 和 Y 轴的偏移量。this.graph.on("translate", ({ tx, ty }) => {self.$emit("saveGraph");});// 移动节点后触发this.graph.on("node:moved", ({ e, x, y, node, view }) => {self.$emit("saveGraph");});// 移动边后触发this.graph.on("edge:moved", ({ e, x, y, node, view }) => {self.$emit("saveGraph");});},// 初始化拖拽initAddon() {this.dnd = new Dnd({target: this.graph,});},// 开始拖拽startDragToGraph() {const node = this.graph.createNode(this.nodeConfig());this.dnd.start(node, this.stencilData.e);},// 节点配置nodeConfig() {const engineItem = this.stencilData.engineItem;const time = new Date().getTime();const attrs = {circle: {r: 4,magnet: true,stroke: "#C2C8D5",strokeWidth: 1,fill: "#fff",},};const top = {position: "top",attrs,};const bottom = {pposition: "bottom",attrs,};const itemsObj = [{id: `in-${time}`,group: "top", // 指定分组名称},{id: `out-${time}`,group: "bottom", // 指定分组名称},];// 链接桩3种状态 1、in | 只允许被连  2、out | 只允许输出  3、any | 不限制let groups = {};let items = [];if (engineItem.top) {groups = {top,};items = [itemsObj[0]];}if (engineItem.bottom) {groups = {bottom,};items = [itemsObj[1]];}if (engineItem.top && engineItem.bottom) {groups = {top,bottom,};items = itemsObj;}let config = {shape: "dag-node",width: 185,height: 40,attrs: {body: {fill: "#1D2035",stroke: "rgba(255, 255, 255, 0.3)",},label: {text: engineItem.name,fill: "rgba(255, 255, 255, 0.9)",},},ports: {groups,items,},data: {label: engineItem.name,engine: engineItem,},};// console.log(config, 33333);return config;},// 初始化节点/边init(data = []) {const cells = [];data.forEach((item) => {if (item.shape === "dag-node") {cells.push(this.graph.createNode(item));} else {cells.push(this.graph.createEdge(item));}});this.graph.resetCells(cells);},// 显示节点状态async showNodeStatus(statusList) {console.log(statusList, "8888888");// const status = statusList.shift();statusList?.forEach((item) => {const { id, status } = item;const node = this.graph.getCellById(id);const data = node.getData();node.setData({...data,status: status,});});this.timer = setTimeout(() => {this.showNodeStatus(statusList);}, 3000);},startFn(item) {this.timer && clearTimeout(this.timer);this.init(item);// this.showNodeStatus(Object.assign([], this.nodeStatusList));this.graph.centerContent();},// 获取画布数据getGraphData() {const { cells = [] } = this.graph.toJSON();let data = [];console.log(cells, 333);for (let i = 0; i < cells.length; i++) {let item = {};let cellsItem = cells[i];if (cellsItem.shape === "dag-node") {let nodeType = 0; // 节点类型 0-下连接柱, 1-上下连接柱 ,2-上连接柱if (cellsItem.ports.items.length === 1 &&cellsItem.ports.items[0].group === "bottom") {nodeType = 0;}if (cellsItem.ports.items.length === 2) {nodeType = 1;}if (cellsItem.ports.items.length === 1 &&cellsItem.ports.items[0].group === "top") {nodeType = 2;}item = {id: cellsItem.id,shape: cellsItem.shape,x: cellsItem.position.x,y: cellsItem.position.y,ports: cellsItem.ports.items,data: {...cellsItem.data,type: "node",nodeType: nodeType,},};} else {item = {id: cellsItem.id,shape: cellsItem.shape,source: cellsItem.source,target: cellsItem.target,data: {type: "edge",},zIndex: 0,};}data.push(item);}return data;},initScroller() {this.graph.use(new Scroller({enabled: true,pageVisible: true,pageBreak: false,pannable: true,}));},// 初始化小地图initMiniMap() {this.graph.use(new MiniMap({container: document.getElementById("minimap"),width: 220,height: 140,padding: 10,}));},},
};
</script><style lang="scss" scoped>
.graph-wrap {width: 100%;height: 100%;min-height: 600px;position: relative;background: #fff;#container {width: 100%;height: 100%;}.right-config {position: absolute;top: 0px;right: 0px;}
}
</style>
<style lang="scss" >
// 小地图
.mini-map-container {position: absolute;bottom: 12px;right: 10px;width: 220px;height: 140px;opacity: 1;// background: #fff;border: 1px solid rgba(255, 255, 255, 0.3);
}.x6-widget-minimap {background: rgba(37, 50, 82, 0.1) !important;
}.x6-widget-minimap-viewport {border: 1px solid #0289f7 !important;
}.x6-widget-minimap-viewport-zoom {border: 1px solid #0289f7 !important;
}
.x6-widget-minimap .x6-graph {box-shadow: none !important;
}.x6-graph-scroller.x6-graph-scroller-paged .x6-graph {box-shadow: none !important;
}// .x6-graph-scroller::-webkit-scrollbar {
//   width: 8px;
//   height: 8px;
//   /**/
// }
// .x6-graph-scroller::-webkit-scrollbar-track {
//   background: rgb(239, 239, 239);
//   border-radius: 2px;
// }
// .x6-graph-scroller::-webkit-scrollbar-thumb {
//   background: #bfbfbf;
//   border-radius: 10px;
// }
// .x6-graph-scroller::-webkit-scrollbar-thumb:hover {
//   background: #999;
// }
// .x6-graph-scroller::-webkit-scrollbar-corner {
//   background: rgb(239, 239, 239);
// }
</style>

toobar.vue 

<template><div class="toolbar"><el-button type="text" :disabled="!canUndo"><el-tooltip effect="dark" content="撤销" placement="right"><i class="raderfont rader-icon-a-revoke" @click="onUndo"></i></el-tooltip></el-button><el-button type="text" :disabled="!canRedo"><el-tooltip effect="dark" content="重做" placement="right"><i class="raderfont rader-icon-next" @click="onRedo"></i></el-tooltip></el-button><el-tooltip effect="dark" content="放大" placement="right"><i class="raderfont rader-icon-amplify" @click="zoomIn"></i></el-tooltip><el-tooltip effect="dark" content="缩小" placement="right"><i class="raderfont rader-icon-reduce" @click="zoomOut"></i></el-tooltip><el-tooltip effect="dark" content="全屏" placement="right"><i class="raderfont rader-icon-full-screen" @click="toFullScreen"></i></el-tooltip></div>
</template><script>
import { History } from "@antv/x6-plugin-history";
export default {name: "Toobar",props: ["graph"],data() {return {graphObj: null,canUndo: false,canRedo: false,};},mounted() {this.$nextTick(() => {this.graphObj = this.graph;this.graphHistory();});},methods: {// 撤销重做graphHistory() {this.graphObj.use(new History({enabled: true,}));this.graphObj.on("history:change", () => {this.canUndo = this.graphObj.canUndo();this.canRedo = this.graphObj.canRedo();});},// 撤销onUndo() {this.graphObj.undo();},// 重做onRedo() {this.graphObj.redo();},// 放大zoomIn() {this.graphObj.zoom(0.2);},// 缩小zoomOut() {this.graphObj.zoom(-0.2);},// 全屏toFullScreen() {this[document.fullscreenElement ? "exitFullscreen" : "fullScreen"]();},fullScreen() {const full = this.$parent.$el;if (full.RequestFullScreen) {full.RequestFullScreen();// 兼容Firefox} else if (full.mozRequestFullScreen) {full.mozRequestFullScreen();// 兼容Chrome, Safari and Opera等} else if (full.webkitRequestFullScreen) {full.webkitRequestFullScreen();// 兼容IE/Edge} else if (full.msRequestFullscreen) {full.msRequestFullscreen();}},exitFullscreen() {if (document.exitFullscreen) {document.exitFullscreen();// 兼容Firefox} else if (document.mozCancelFullScreen) {document.mozCancelFullScreen();// 兼容Chrome, Safari and Opera等} else if (document.webkitExitFullscreen) {document.webkitExitFullscreen();// 兼容IE/Edge} else if (document.msExitFullscreen) {document.msExitFullscreen();}},},
};
</script><style lang="scss" scoped>
.toolbar {z-index: 100;position: absolute;top: 50%;transform: translateY(-50%);left: 16px;display: flex;flex-direction: column;justify-content: center;align-items: center;background: rgba(255, 255, 255, 0.2);border-radius: 4px;box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.06);.el-button + .el-button {margin-left: 0px;}.el-button {margin: 5px 0px;}i {font-size: 18px;margin: 5px 8px;// color: rgba(255, 255, 255, 0.8);cursor: pointer;&:hover {color: #1890ff;}}.layout-opts {list-style: none;padding: 0;text-align: center;li {cursor: pointer;font-size: 14px;line-height: 22px;color: #3c5471;&:hover {color: #1890ff;}}}
}
</style>

dataBase.vue 

<template><divclass="node":class="[status === 0 ? 'running' : '',status === 1 ? 'progress' : '',status === 2 ? 'success' : '',status === 3 ? 'failed' : '',status === 4 ? 'stop' : '',]"><span class="left" :class="[labelList.includes(label) ? 'common' : '']"><img v-if="labelList.includes(label)" :src="leftImg[label]" alt="" /><imgv-if="!labelList.includes(label)"src="@/static/images/detection.png"alt=""/></span><span class="right"><span class="label" :title="label">{{ label }}</span><span class="status"><img :src="imgCot[status]" alt="" /></span></span></div>
</template><script>
export default {name: "DataBase",inject: ["getNode"],data() {return {status: 0,label: "",labelList: ["开始", "结束", "过滤器", "选择器"],imgCot: {0: require("@/static/images/wait-status.png"),1: require("@/static/images/progress-status.png"),2: require("@/static/images/success-status.png"),3: require("@/static/images/fail-status.png"),4: require("@/static/images/stop-status.png"),5: require("@/static/images/pause-status.png"),},leftImg: {开始: require("@/static/images/start-inside.png"),结束: require("@/static/images/stop-inside.png"),过滤器: require("@/static/images/filter-inside.png"),选择器: require("@/static/images/selector-inside.png"),},};},computed: {showStatus() {if (typeof this.status === "undefined") {return false;}return true;},},mounted() {const self = this;const node = this.getNode();this.label = node.data.label;this.status = node.data.status || 0;// console.log(node, 11111);// 监听数据改变事件node.on("change:data", ({ current }) => {console.log(current, 22222);self.label = current.label;self.status = current.status;});},methods: {},
};
</script><style lang="scss" scoped>
.node {display: flex;align-items: center;width: 100%;height: 100%;background-color: #fff;// border: 1px solid rgba(255, 255, 255, 0.3);// border-left: 4px solid #5f95ff;border-radius: 8px;box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06);.left {flex-shrink: 0;display: flex;align-items: center;justify-content: center;width: 40px;height: 40px;border-radius: 8px 0px 0px 8px;box-sizing: border-box;border: 1px solid rgba(220, 223, 230);// background: rgba(42, 230, 255, 0.15);&.common {// background: rgba(168, 237, 113, 0.149);}img {width: 22px;height: 22px;}}.right {height: 100%;flex: 1;display: flex;align-items: center;justify-content: space-between;border: 1px solid rgba(220, 223, 230);border-radius: 0px 8px 8px 0px;border-left: 0;padding: 0px 5px;.label {flex: 1;display: inline-block;flex-shrink: 0;// color: rgba(255, 255, 255, 0.9);color: #666;font-size: 12px;overflow: hidden; //超出文本隐藏text-overflow: ellipsis; ///超出部分省略号显示display: -webkit-box; //弹性盒模型-webkit-box-orient: vertical; //上下垂直-webkit-line-clamp: 2; //自定义行数}.status {width: 18px;height: 18px;flex-shrink: 0;margin-left: 5px;img {width: 18px;height: 18px;}}}
}.node.success {// border-left: 4px solid #52c41a;
}.node.failed {// border-left: 4px solid #ff4d4f;
}.node.progress .status img {animation: spin 1s linear infinite;
}.x6-node-selected .node {border-color: #2ae6ff;border-radius: 8px;box-shadow: 0 0 0 3px #d4e8fe;
}.x6-node-selected .node.running {border-color: #2ae6ff;border-radius: 8px;// box-shadow: 0 0 0 4px #ccecc0;
}.x6-node-selected .node.success {border-color: #52c41a;border-radius: 8px;// box-shadow: 0 0 0 4px #ccecc0;
}.x6-node-selected .node.failed {border-color: #ff4d4f;border-radius: 8px;// box-shadow: 0 0 0 4px #fedcdc;
}.x6-edge:hover path:nth-child(2) {stroke: #1890ff;stroke-width: 1px;
}.x6-edge-selected path:nth-child(2) {stroke: #1890ff;stroke-width: 1.5px !important;
}@keyframes running-line {to {stroke-dashoffset: -1000;}
}@keyframes spin {from {transform: rotate(0deg);}to {transform: rotate(360deg);}
}
</style>

contextmenu.vue

<template><ul class="contextmenu-wrap" :style="{ left: x + 'px', top: y + 'px' }"><li @click.stop="callBack('remove')">删除</li></ul>
</template><script>
export default {name: "Contextmenu",data() {return {x: "",y: "",item: {}, // 节点或者边的数据};},mounted() {},methods: {initFn(x, y, item) {this.x = parseInt(x) + "";this.y = parseInt(y) + "";if (item) {this.item = item;}},callBack(type) {this.$emit("callBack", type, this.item);},},
};
</script><style lang="scss" scoped>
.contextmenu-wrap {width: 150px;position: fixed;z-index: 999;// border: 1px solid rgba(255, 255, 255, 0.3);border-radius: 4px;font-size: 12px;color: #545454;background: #1d2035;padding: 10px 8px;box-shadow: rgb(174, 174, 174) 0px 0px 10px;> li {color: #ffffff;cursor: pointer;text-align: center;// background: rgba(37, 50, 82, 0.2);}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/657219.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的游泳会员管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环境配…

【云原生】consul自动注册,实现负载均衡器与节点服务应用解耦,批量管理容器

目录 一、consul解决了什么问题&#xff1f; 二、consul的模式 三、consul的工作原理 四、实操consul连接负载均衡与容器 步骤一&#xff1a;完成consul的部署 步骤二&#xff1a;完成gliderlabs/registrator:latest镜像的拉取&#xff0c;并完成启动 步骤三&#xff1a;…

重写Sylar基于协程的服务器(1、日志模块的架构)

重写Sylar基于协程的服务器&#xff08;1、日志模块的架构&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、日志模…

15EG在PL端使用外部时钟驱动led灯

创建PL端的vivado工程在 pl_only_led中已经介绍过了&#xff0c;这里直接从创建好设计文件开始 打开设计文件修改里面代码&#xff0c;代码我也提供了&#xff0c;在工程文件夹下的file文件夹中 15eg这块板子有俩个外部晶振&#xff0c;分别时200M和74.25 M我们可以在原理图中找…

16- OpenCV:轮廓的发现和轮廓绘制、凸包

目录 一、轮廓发现 1、轮廓发现(find contour in your image) 的含义 2、相关的API 以及代码演示 二、凸包 1、凸包&#xff08;Convex Hull&#xff09;的含义 2、Graham扫描算法- 概念介绍 3、cv::convexHull 以及代码演示 三、轮廓周围绘制矩形和圆形框 一、轮廓发现…

【图文详解】阿里云服务器放行高防IP加入安全组

打开阿里云的云服务器配置面板&#xff0c;在要操作实例的操作列找到更多 > 网络和安全组 > 安全组配置。 对已有安全组配置规则&#xff0c;或者直接添加安全组规则。 根据需要放通高防IP在内的IP段相应协议类型的端口访问。

QEMU - e1000全虚拟化前端与TAP/TUN后端流程简析

目录 1. Host -> Guest 2.Guest ->Host 3. 如何修改以支持TUN设备的后端&#xff1f; 4. 相关 QEMU 源码 5. 实验 1. Host -> Guest 2.Guest ->Host 3. 如何修改以支持TUN设备的后端&#xff1f; 1. 简单通过后端网卡名字来判断是TUN还是TAP。 2. 需要前端全…

Adobe Camera Raw forMac/win:掌控原始之美的秘密武器

Adobe Camera Raw&#xff0c;这款由Adobe开发的插件&#xff0c;已经成为摄影师和设计师们的必备工具。对于那些追求完美、渴望探索更多创意可能性的专业人士来说&#xff0c;它不仅仅是一个插件&#xff0c;更是一个能够释放无尽创造力的平台。 在数字摄影时代&#xff0c;R…

使用Win32API实现贪吃蛇小游戏

目录 C语言贪吃蛇项目 基本功能 需要的基础内容 Win32API 介绍 控制台程序部分指令 设置控制台窗口的长宽 设置控制台的名字 控制台在屏幕上的坐标位置结构体COORD 检索指定标准设备的句柄&#xff08;标准输入、标准输出或标准错误&#xff09; 光标信息结构体类型CONSOLE_CUR…

洛谷P8599 [蓝桥杯 2013 省 B] 带分数

[蓝桥杯 2013 省 B] 带分数 题目描述 100 100 100 可以表示为带分数的形式&#xff1a; 100 3 69258 714 100 3 \frac{69258}{714} 100371469258​。 还可以表示为&#xff1a; 100 82 3546 197 100 82 \frac{3546}{197} 100821973546​。 注意特征&#xff1a;带分…

条款32:确定你的public继承塑模出 is-a 关系

如果你编写类D(“派生类”)public继承类B(“基类”)&#xff0c;就是在告诉C编译器(以及代码的读者)每个类型D的对象都是类型B的对象&#xff0c;但反之则不然。 class Person {...}; class Student: public Person {...}; void eat(const Person& p); // 素有的Person都…

云计算HCIE备考经验分享

大家好&#xff0c;我是来自深圳信息职业技术学院22级鲲鹏3-1班的刘同学&#xff0c;在2023年9月19日成功通过了华为云计算HCIE认证&#xff0c;并且取得了A的成绩。下面把我的考证经验分享给大家。 转专业进鲲鹏班考HCIE 大一上学期的时候&#xff0c;在上Linux课程的时候&…

ES Serverless让日志检索更加便捷

前言 在项目中,或者开发过程中,出现bug或者其他线上问题,开发人员可以通过查看日志记录来定位问题。通过日志定位 bug 是一种常见的软件开发和运维技巧,只有观察日志才能追踪到具体代码。在软件开发过程中,开发人员会在代码中添加日志记录,以记录程序的运行情况和异常信…

发现了一款宝藏学习项目,包含了Web全栈的知识体系,JS、Vue、React知识就靠它了!

前言 在当今互联网时代&#xff0c;一切以页面、UI为主要呈现方式&#xff0c;web全栈开发工程师的需求越来越大。 然而&#xff0c;市场上大多数工程师只会使用api而不了解其原理&#xff0c;这种情况使得他们变得可替代。 因此&#xff0c;成为一个高级开发工程师需要具备…

用React给XXL-JOB开发一个新皮肤(四):实现用户管理模块

目录 一. 简述二. 模块规划 2.1. 页面规划2.2. 模型实体定义 三. 模块实现 3.1. 用户分页搜索3.2. Modal 配置3.3. 创建用户表单3.4. 修改用户表单3.5. 删除 四. 结束语 一. 简述 上一篇文章我们实现登录页面和管理页面的 Layout 骨架&#xff0c;并对接登录和登出接口。这篇…

在centos 7 中 安装 配置 并 远程连接 MySQL5.7

目录 安装MySQL 1.卸载CentOS7系统自带的mariadb 2.安装依赖库 3.上传MySQL并解压 4.安装MySQL 配置MySQL 1.修改登录密码 2.修改字符集 3.配置远程连接 前言&#xff1a; 安装MySQL版本&#xff1a;mysql-5.7.30-1.el7.x86_64.rpm-bundle 文件需求后台私信 以下7条为…

STM32学习笔记(四) —— 位段别名区的使用

STM32F103RCT6有两个位段区 (SRAM 最低1M空间和片内外设存储区最低1M空间)&#xff0c; 这两个区域都有各自的别名区&#xff0c;在别名区中每个字会映射到位段区的一个位&#xff0c;所以在别名区修改一个字相当于修改位段区中对应的一个位 映射公式( 别名区中的字与位段区中的…

实现vue3响应式系统核心-watch

简介 今天我们来看看 watch 的实现。 watch本质就是观测一个响应式数据&#xff0c;当数据发生变化时通知并执行相应的回调函数。实际上&#xff0c;watch的实现本质上就是利用了 effect 以及 options.scheduler选项。 代码地址&#xff1a; https://github.com/SuYxh/share-…

flask基于python的个人理财备忘录记账提醒系统vue

在当今高度发达的信息中&#xff0c;信息管理改革已成为一种更加广泛和全面的趋势。 “备忘记账系统”是基于Mysql数据库&#xff0c;在python程序设计的基础上实现的。为确保中国经济的持续发展&#xff0c;信息时代日益更新&#xff0c;蓬勃发展。同时&#xff0c;随着信息社…

【智能家居入门2】(MQTT协议、微信小程序、STM32、ONENET云平台)

此篇智能家居入门与前两篇类似&#xff0c;但是是使用MQTT协议接入ONENET云平台&#xff0c;实现微信小程序与下位机的通信&#xff0c;这里相较于使用http协议的那两篇博客&#xff0c;在主程序中添加了独立看门狗防止程序卡死和服务器掉线问题。后续还有使用MQTT协议连接MQTT…