当前位置: 首页 > news >正文

Vue3中使用LogicFlow实现简单流程图

实现结果

在这里插入图片描述
实现功能:

  1. 拖拽创建节点
  2. 自定义节点/边
  3. 自定义快捷键
  4. 人员选择弹窗
  5. 右侧动态配置组件
  6. 配置项获取/回显
  7. 必填项验证

自定义节点与拖拽创建节点

拖拽节点面板node-panel.vue

<template><div class="node-panel"><divv-for="(item, key) in state.nodePanel":key="key"class="approve-node"@mousedown="dragNode(item)"><div class="node-shape" :class="'node-' + item.type"></div><div class="node-label">{{ item.text }}</div></div></div>
</template><script lang="ts" setup>
import { ILogicFlowNodePanelItem } from "@/types/logic-flow";
import LogicFlow from "@logicflow/core";
import { reactive } from "vue";
const props = defineProps<{ lf?: LogicFlow }>();
const state = reactive({nodePanel: [{type: "approver",text: "用户活动",},{type: "link",text: "连接点",},{type: "review",text: "传阅",},],
});
const dragNode = (item: ILogicFlowNodePanelItem) => {props.lf?.dnd.startDrag({type: item.type,text: item.text,});
};
</script>

自定义节点/边index.ts

/*** @description 注册节点* @export* @param {LogicFlow} lf* @return {*}*/
export function registeNode(lf: ShallowRef<LogicFlow | undefined>) {/*** @description 自定义开始节点*/class StartNode extends CircleNode {getShape() {const { x, y } = this.props.model;const style = this.props.model.getNodeStyle();return h("g", {}, [h("circle", {...style,cx: x,cy: y,r: 30,stroke: "#000",fill: "#000",}),]);}getText() {const { x, y, text } = this.props.model;return h("text",{x: x,y: y,fill: "#fff",textAnchor: "middle",alignmentBaseline: "middle",style: { fontSize: 12 },},text.value);}}class StartNodeModel extends CircleNodeModel {setAttributes() {this.r = 30;this.isSelected = false;}getConnectedTargetRules() {const rules = super.getConnectedTargetRules();const geteWayOnlyAsTarget = {message: "开始节点只能连出,不能连入!",validate: (source?: BaseNodeModel, target?: BaseNodeModel) => {let isValid = true;if (target) {isValid = false;}return isValid;},};rules.push(geteWayOnlyAsTarget);return rules;}getConnectedSourceRules() {const rules = super.getConnectedSourceRules();const onlyOneOutEdge = {message: "开始节点只能连出一条线!",validate: (source?: BaseNodeModel, target?: BaseNodeModel) => {let isValid = true;if (source?.outgoing.edges.length) {isValid = false;}return isValid;},};rules.push(onlyOneOutEdge);return rules;}createId() {return uuidv4();}}lf.value?.register({type: "start",view: StartNode,model: StartNodeModel,});/*** @description 自定义发起节点*/class LaunchNode extends RectNode {getShape() {const { x, y, width, height, radius } = this.props.model;const style = this.props.model.getNodeStyle();return h("g", {}, [h("rect", {...style,x: x - width / 2,y: y - height / 2,rx: radius,ry: radius,width: 120,height: 50,stroke: "#000",fill: "#000",}),]);}getText() {const { x, y, text, width, height } = this.props.model;return h("foreignObject",{x: x - width / 2,y: y - height / 2,className: "foreign-object",style: {width: width,height: height,},},[h("p",{style: {fontSize: 12,width: width,height: height,lineHeight: height + "px",whiteSpace: "nowrap",overflow: "hidden",textOverflow: "ellipsis",textAlign: "center",padding: "0 8px",boxSizing: "border-box",margin: "0",color: "#fff",},},text.value),]);}}class LaunchModel extends RectNodeModel {setAttributes() {this.width = 120;this.height = 50;this.radius = 4;this.isSelected = false;}getConnectedSourceRules() {const rules = super.getConnectedSourceRules();const notAsTarget = {message: "不能连接自己",validate: (source?: BaseNodeModel, target?: BaseNodeModel) => {let isValid = true;if (source?.id === target?.id) {isValid = false;}return isValid;},};rules.push(notAsTarget);return rules;}createId() {return uuidv4();}}lf.value?.register({type: "launch",view: LaunchNode,model: LaunchModel,});/*** @description 自定义审批节点*/class ApproverNode extends RectNode {getShape() {const { x, y, width, height, radius } = this.props.model;const style = this.props.model.getNodeStyle();return h("g", {}, [h("rect", {...style,x: x - width / 2,y: y - height / 2,rx: radius,ry: radius,width: 120,height: 50,stroke: "#facd91",fill: "#facd91",}),]);}getText() {const { x, y, text, width, height } = this.props.model;return h("foreignObject",{x: x - width / 2,y: y - height / 2,className: "foreign-object",style: {width: width,height: height,},},[h("p",{style: {fontSize: 12,width: width,height: height,lineHeight: height + "px",whiteSpace: "nowrap",overflow: "hidden",textOverflow: "ellipsis",textAlign: "center",padding: "0 8px",boxSizing: "border-box",margin: "0",},},text.value),]);}}class ApproverModel extends RectNodeModel {setAttributes() {this.width = 120;this.height = 50;this.radius = 4;this.isSelected = false;}getConnectedSourceRules() {const rules = super.getConnectedSourceRules();const notAsTarget = {message: "不能连接自己",validate: (source?: BaseNodeModel, target?: BaseNodeModel) => {let isValid = true;if (source?.id === target?.id) {isValid = false;}return isValid;},};rules.push(notAsTarget);return rules;}createId() {return uuidv4();}}lf.value?.register({type: "approver",view: ApproverNode,model: ApproverModel,});/*** @description 自定义连接点节点*/class LinkNode extends RectNode {getShape() {const { x, y, width, height, radius } = this.props.model;const style = this.props.model.getNodeStyle();return h("g", {}, [h("rect", {...style,x: x - width / 2,y: y - height / 2,rx: radius,ry: radius,width: 120,height: 50,stroke: "#caf982",fill: "#caf982",}),]);}getText() {const { x, y, text, width, height } = this.props.model;return h("foreignObject",{x: x - width / 2,y: y - height / 2,className: "foreign-object",style: {width: width,height: height,},},[h("p",{style: {fontSize: 12,width: width,height: height,lineHeight: height + "px",whiteSpace: "nowrap",overflow: "hidden",textOverflow: "ellipsis",textAlign: "center",padding: "0 8px",boxSizing: "border-box",margin: "0",},},text.value),]);}}class LinkModel extends RectNodeModel {setAttributes() {this.width = 120;this.height = 50;this.radius = 4;this.isSelected = false;}getConnectedSourceRules() {const rules = super.getConnectedSourceRules();const notAsTarget = {message: "不能连接自己",validate: (source?: BaseNodeModel, target?: BaseNodeModel) => {let isValid = true;if (source?.id === target?.id) {isValid = false;}return isValid;},};rules.push(notAsTarget);return rules;}createId() {return uuidv4();}}lf.value?.register({type: "link",view: LinkNode,model: LinkModel,});/*** @description 自定义传阅节点*/class ReviewNode extends RectNode {getShape() {const { x, y, width, height, radius } = this.props.model;const style = this.props.model.getNodeStyle();return h("g", {}, [h("rect", {...style,x: x - width / 2,y: y - height / 2,rx: radius,ry: radius,width: 120,height: 50,stroke: "#81d3f8",fill: "#81d3f8",}),]);}getText() {const { x, y, text, width, height } = this.props.model;return h("foreignObject",{x: x - width / 2,y: y - height / 2,className: "foreign-object",style: {width: width,height: height,},},[h("p",{style: {fontSize: 12,width: width,height: height,lineHeight: height + "px",whiteSpace: "nowrap",overflow: "hidden",textOverflow: "ellipsis",textAlign: "center",padding: "0 8px",boxSizing: "border-box",margin: "0",},},text.value),]);}}class ReviewModel extends RectNodeModel {setAttributes() {this.width = 120;this.height = 50;this.radius = 4;this.isSelected = false;}getConnectedSourceRules() {const rules = super.getConnectedSourceRules();const notAsTarget = {message: "不能连接自己",validate: (source?: BaseNodeModel, target?: BaseNodeModel) => {let isValid = true;if (source?.id === target?.id) {isValid = false;}return isValid;},};rules.push(notAsTarget);return rules;}createId() {return uuidv4();}}lf.value?.register({type: "review",view: ReviewNode,model: ReviewModel,});/*** @description 结束节点*/class FinishNode extends CircleNode {getShape() {const { x, y } = this.props.model;const style = this.props.model.getNodeStyle();return h("g", {}, [h("circle", {...style,cx: x,cy: y,r: 30,stroke: "#000",fill: "#000",}),]);}getText() {const { x, y, text } = this.props.model;return h("text",{x: x,y: y,fill: "#fff",textAnchor: "middle",alignmentBaseline: "middle",style: { fontSize: 12 },},text.value);}}class FinishModel extends CircleNodeModel {setAttributes() {this.r = 30;this.isSelected = false;}getConnectedSourceRules() {const rules = super.getConnectedSourceRules();const notAsTarget = {message: "终止节点不能作为连线的起点",validate: () => false,};rules.push(notAsTarget);return rules;}createId() {return uuidv4();}}lf.value?.register({type: "end",view: FinishNode,model: FinishModel,});/*** @description 虚线*/class DashedLineModel extends PolylineEdgeModel {getEdgeStyle() {const style = super.getEdgeStyle();style.stroke = "#000";style.strokeDasharray = "3 3";return style;}}lf.value?.register({type: "dashedLine",view: PolylineEdge,model: DashedLineModel,});/*** @description 开始的连线*/class StartPolylineModel extends PolylineEdgeModel {setAttributes() {this.isSelected = false;this.isHitable = false;}}lf.value?.register({type: "startPolyline",view: PolylineEdge,model: StartPolylineModel,});
}

注册logicflow并使用自定义节点

<template><div class="logic-flow-container"><div class="logic-flow-header"><el-button type="primary" @click="getData">获取数据</el-button><el-button type="primary" @click="submit">提交</el-button></div><div class="logic-flow-main"><div class="logic-flow" ref="logicFlowRef"></div><Settingclass="logic-flow-setting":data="nodeData!":lf="lf":type="state.settingType"></Setting><NodePanel :lf="lf"></NodePanel></div><!-- 当lf有值 才能注册事件 --><Control v-if="lf" :lf="lf"></Control></div>
</template><script lang="ts">
export default { name: "LogicFlow" };
</script>
<script lang="ts" setup>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import "@logicflow/extension/lib/style/index.css";
import { onMounted, reactive, ref, ShallowRef, shallowRef } from "vue";
import NodePanel from "./components/node-panel.vue";
import { registeNode, registerKeyboard, requiredConfig } from "./index";
import { ElMessage } from "element-plus";
import Control from "./components/control.vue";
import Setting from "./components/setting.vue";
import { SettingType } from "@/types/logic-flow";const logicFlowRef = ref<HTMLDivElement>();
const nodeData = ref<LogicFlow.NodeData | LogicFlow.EdgeData>(); // 节点数据
const state = reactive({settingType: "all" as SettingType,
});
const lf = shallowRef<LogicFlow>();const getSettingInfo = (data: LogicFlow.NodeData | LogicFlow.EdgeData) => {switch (data.type) {case "launch":nodeData.value = data;state.settingType = data.type;break;case "approver":nodeData.value = data;state.settingType = data.type;break;case "link":nodeData.value = data;state.settingType = data.type;break;case "review":nodeData.value = data;state.settingType = data.type;break;case "polyline":case "dashedLine":nodeData.value = data;state.settingType = data.type;break;}
};
/*** @description 注册事件*/
const initEvent = (lf: ShallowRef<LogicFlow | undefined>) => {lf.value?.on("blank:click", (e) => {state.settingType = "all";});lf.value?.on("node:mousedown", ({ data }) => {lf.value?.selectElementById(data.id, false);getSettingInfo(data);});lf.value?.on("edge:click", ({ data }) => {lf.value?.selectElementById(data.id, false);getSettingInfo(data);});lf.value?.on("connection:not-allowed", (data) => {ElMessage.error(data.msg);return false;});lf.value?.on("node:dnd-add", ({ data }) => {// 选中节点 更改信息lf.value?.selectElementById(data.id, false);getSettingInfo(data);lf.value?.container.focus(); // 聚焦 能够使用键盘操作});
};
/*** @description 获取数据*/
const getData = () => {console.log(lf.value?.getGraphData());
};
/*** @description 提交 验证数据*/
const submit = () => {const { nodes } = lf.value?.getGraphData() as LogicFlow.GraphData;for (let index = 0; index < nodes.length; index++) {const data = nodes[index];const { properties } = data;// 循环配置项for (const key in properties) {// 数组配置项 判断是否为空if (Array.isArray(properties[key])) {if (requiredConfig[key] && properties[key].length === 0) {return ElMessage.error(`${data.text?.value}节点 ${requiredConfig[key]}`);}} else {// 非数组配置项 判断是否为空if (requiredConfig[key] && !properties[key]) {return ElMessage.error(`${data.text?.value}节点 ${requiredConfig[key]}`);}}}}console.log(lf.value?.getGraphData());
};onMounted(() => {lf.value = new LogicFlow({container: logicFlowRef.value!,grid: true,keyboard: {enabled: true,shortcuts: registerKeyboard(lf, nodeData),},textEdit: false,});registeNode(lf);initEvent(lf);lf.value.render({nodes: [{id: "node_1",type: "start",x: 100,y: 300,properties: {width: 60,height: 60,},text: {x: 100,y: 300,value: "开始",},},{id: "node_2",type: "launch",x: 100,y: 400,properties: {width: 120,height: 50,},text: {x: 100,y: 400,value: "发起流程",},},{id: "node_3",type: "end",x: 100,y: 600,properties: {width: 60,height: 60,},text: {x: 100,y: 600,value: "结束",},},],edges: [{id: "edge_1",type: "startPolyline",sourceNodeId: "node_1",targetNodeId: "node_2",},{id: "edge_2",type: "polyline",sourceNodeId: "node_2",targetNodeId: "node_3",},],});lf.value.translateCenter(); // 将图形移动到画布中央
});
</script>

右侧的配置设置

  1. 通过componentIs实现不同的配置组件
  2. 通过logicflow的setProperties()函数,将配置项注入节点/边的properties对象中,目的是传参和回显的时候方便
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

人员选择组件

正选、反选、回显,可作为一个单独组件使用,目前使用的是el-tree,数据量大时可考虑虚拟树
在这里插入图片描述

<template><MyDialogv-model="state.visible"title="选择人员"width="800px"@close="close"@cancel="close"@submit="submit"><div class="type"><label><span>发起人:</span><el-radio-group v-model="state.type"><el-radio value="1">指定人员</el-radio><el-radio value="2">角色</el-radio></el-radio-group></label></div><div class="panel"><div class="left-panel"><div class="panel-title">人员选择</div><div class="search"><el-inputv-model="state.filterText"style="width: 100%"placeholder="请输入筛选内容"/></div><div class="content"><el-treeref="treeRef":data="state.data"show-checkboxnode-key="key":check-on-click-node="true":filter-node-method="filterNode"@check-change="checkChange"/></div></div><div class="right-panel"><div class="panel-title">已选择</div><div class="content checked-content"><el-tagv-for="tag in state.checkedList":key="tag.key"closabletype="primary"@close="handleClose(tag.key)">{{ tag.label }}</el-tag></div></div></div></MyDialog>
</template><script lang="ts">
export default { name: "ChoosePerson" };
</script>
<script lang="ts" setup>
import { ElTree } from "element-plus";
import { nextTick, reactive, ref, watch } from "vue";
interface Tree {[key: string]: any;
}
const state = reactive({visible: false,type: "1",filterText: "",value: [],data: [{label: "张三",key: "1",},{label: "李四",key: "2",},{label: "王五",key: "3",children: [{label: "王五1",key: "31",},{label: "王五2",key: "32",},],},],checked: [] as string[],checkedList: [] as { label: string; key: string }[],
});
const treeRef = ref<InstanceType<typeof ElTree>>();
const emits = defineEmits(["submit"]);
/*** @description 筛选节点*/
watch(() => state.filterText,(val) => {treeRef.value!.filter(val);}
);
const open = (checked: string[]) => {state.visible = true;nextTick(() => {state.checked = checked;treeRef.value?.setCheckedKeys([...checked], false);});
};
const close = () => {state.visible = false;state.filterText = "";
};
const submit = () => {emits("submit", state.checked, state.checkedList);close();
};
/*** @description 筛选节点*/
const filterNode = (value: string, data: Tree) => {if (!value) return true;return data.label.includes(value);
};
/*** @description 选中节点*/
const checkChange = () => {// 已选的id string[] 用来提交state.checked = treeRef.value?.getCheckedNodes(true, false).map((item) => item.key) as string[];// 已选的对象 {label: string; key: string}[] 用来展示tagstate.checkedList = treeRef.value?.getCheckedNodes(true, false).map((item) => {return {label: item.label,key: item.key,};})!;
};
/*** @description 删除已选人员*/
const handleClose = (key: string) => {state.checkedList = state.checkedList.filter((item) => item.key !== key);treeRef.value?.setCheckedKeys(state.checkedList.map((item) => item.key),false);
};
/*** @description 清空已选人员*/
const clear = () => {state.checkedList = [];state.checked = [];treeRef.value?.setCheckedKeys([], false);
};
defineExpose({open,clear,
});
</script><style lang="scss" scoped>
.type {display: flex;align-items: center;margin-bottom: 20px;span {margin-right: 10px;}label {display: flex;align-items: center;}
}
.panel {width: 100%;display: flex;.left-panel {flex: 1;border: 1px solid #ccc;border-radius: 4px;.search {padding: 6px 10px;}}.right-panel {flex: 1;margin-left: 20px;border: 1px solid #ccc;border-radius: 4px;}.panel-title {padding: 10px 0;font-size: 14px;font-weight: bold;background-color: #f5f5f5;text-align: center;}.content {max-height: 400px;min-height: 200px;overflow: auto;}.checked-content {padding: 6px 10px;.el-tag + .el-tag {margin-left: 10px;}}
}
</style>

自定义快捷键,根据源码改编

/*** @description 注册键盘事件* @export* @param {(ShallowRef<LogicFlow | undefined>)} lf* @param {(Ref<LogicFlow.NodeData | LogicFlow.EdgeData | undefined>)} nodeData* @return {*}*/
export function registerKeyboard(lf: ShallowRef<LogicFlow | undefined>,nodeData: Ref<LogicFlow.NodeData | LogicFlow.EdgeData | undefined>
) {let copyNodes = undefined as LogicFlow.NodeData[] | undefined;let TRANSLATION_DISTANCE = 40;let CHILDREN_TRANSLATION_DISTANCE = 40;const cv = [{keys: ["ctrl + c", "cmd + c"],callback: () => {copyNodes = lf.value?.getSelectElements().nodes;},},{keys: ["ctrl + v", "cmd + v"],callback: () => {const startOrEndNode = copyNodes?.find((node) =>node.type === "start" ||node.type === "end" ||node.type === "launch");if (startOrEndNode) {return true;}if (copyNodes) {lf.value?.clearSelectElements();copyNodes.forEach(function (node) {node.x += TRANSLATION_DISTANCE;node.y += TRANSLATION_DISTANCE;node.text!.x += TRANSLATION_DISTANCE;node.text!.y += TRANSLATION_DISTANCE;return node;});let addElements = lf.value?.addElements({ nodes: copyNodes, edges: [] },CHILDREN_TRANSLATION_DISTANCE);if (!addElements) return true;addElements.nodes.forEach(function (node) {nodeData.value = node.getData();return lf.value?.selectElementById(node.id, true);});CHILDREN_TRANSLATION_DISTANCE =CHILDREN_TRANSLATION_DISTANCE + TRANSLATION_DISTANCE;}return false;},},{keys: ["backspace"],callback: () => {const elements = lf.value?.getSelectElements(true);if (elements) {lf.value?.clearSelectElements();elements.edges.forEach(function (edge) {return edge.id && lf.value?.deleteEdge(edge.id);});elements.nodes.forEach(function (node) {if (node.type === "start" ||node.type === "end" ||node.type === "launch") {return true;}return node.id && lf.value?.deleteNode(node.id);});return false;}},},];return cv;
}

仓库地址
在线预览

相关文章:

Vue3中使用LogicFlow实现简单流程图

实现结果 实现功能&#xff1a; 拖拽创建节点自定义节点/边自定义快捷键人员选择弹窗右侧动态配置组件配置项获取/回显必填项验证 自定义节点与拖拽创建节点 拖拽节点面板node-panel.vue <template><div class"node-panel"><divv-for"(item, k…...

《重学Java设计模式》之 工厂方法模式

《重学Java设计模式》之 建造者模式 《重学Java设计模式》之 原型模式 《重学Java设计模式》之 单例模式 模拟发奖多种商品 工程结构 奖品发放接口 package com.yys.mes.design.factory.store;public interface ICommodity {/*** Author Sherry* Date 14:20 2024/11/6**/voi…...

【大数据学习 | kafka】kafka的数据存储结构

以上是kafka的数据的存储方式。 这些数据可以在服务器集群上对应的文件夹中查看到。 [hexuanhadoop106 __consumer_offsets-0]$ ll 总用量 8 -rw-rw-r--. 1 hexuan hexuan 10485760 10月 28 22:21 00000000000000000000.index -rw-rw-r--. 1 hexuan hexuan 0 10月 28 …...

知识竞赛答题系统,线上答题小程序链接怎么做?

随着智能手机的普及&#xff0c;越来越多的单位开始在线上开展知识竞赛。这种形式的知识竞赛不仅易于操作&#xff0c;而且参与度更高。那么线上知识竞赛答题系统怎么做呢&#xff1f;自己可以做吗&#xff1f;答案是可以的&#xff01;借助微信答题系统制作平台风传吧&#xf…...

基于SSM的社区物业管理系统+LW参考示例

1.项目介绍 系统角色&#xff1a;管理员、业主&#xff08;普通用户&#xff09;功能模块&#xff1a;管理员&#xff08;用户管理、二手置换管理、报修管理、缴费管理、公告管理&#xff09;、普通用户&#xff08;登录注册、二手置换、生活缴费、信息采集、报事报修&#xf…...

android——jetpack startup初始化框架

一、jetpack startup Android Jetpack Startup是一个库&#xff0c;它简化了Android应用启动过程&#xff0c;尤其是对于那些需要处理复杂数据绑定和初始化逻辑的应用。它的核心在于提供了一个StartupComponent&#xff0c;用于声明应用的初始化逻辑&#xff0c;这个逻辑会在首…...

英伟达HOVER——用于人形机器人的多功能全身控制器:整合不同的控制模式且实现彼此之间的无缝切换

前言 前几天&#xff0c;一在长沙的朋友李总发我一个英伟达HOVER的视频(自从我今年年初以来持续不断的解读各大顶级实验室的最前沿paper、以及分享我司七月在具身领域的探索与落地后&#xff0c;影响力便越来越大了&#xff0c;不断加油 )&#xff0c;该视频说的有点玄乎&…...

GEE代码学习 day17

13.2 地球上到处都有许多图像吗&#xff1f; 我们可以使用下面的代码将这个 reducer count 应用于我们过滤后的 ImageCollection。我们将返回相同的数据集并筛选 2020 年&#xff0c;但没有地理限制。这将收集来自世界各地的图像&#xff0c;然后计算每个像素中的图像数量。以…...

论文阅读笔记-Covariate Shift: A Review and Analysis on Classifiers

前言 标题&#xff1a;Covariate Shift: A Review and Analysis on Classifiers 原文链接&#xff1a;Link\ 我们都知道在机器学习模型中&#xff0c;训练数据和测试数据是不同的阶段&#xff0c;并且&#xff0c;通常是是假定训练数据和测试数据点遵循相同的分布。但是实际上&…...

基于SSM+VUE守护萌宠宠物网站JAVA|VUE|Springboot计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…...

【在Linux世界中追寻伟大的One Piece】Socket编程TCP

目录 1 -> TCP socket API 2 -> V1 -Echo Server 2.1 -> 测试多个连接的情况 1 -> TCP socket API socket()&#xff1a; socket()打开一个网络通讯端口&#xff0c;如果成功的话&#xff0c;就像open()一样返回一个文件描述符。应用程序可以像读写文件一样用r…...

进入半导体行业需要具备哪些能力?

要进入半导体公司&#xff0c;尤其是从事工艺流程设计和制程优化的岗位&#xff0c;需要具备一定的跨学科背景。 以某公司招聘要求为例&#xff1a; **公司 招聘岗位&#xff1a;工艺工程师 该公司是一家从事半导体设备、工艺与材料研发、生产和销售的公司&#xff0c;面向…...

Nature重磅:AI化学家再升级!大幅提升实验效率,推动化学合成进入“智能化”新阶段

人工智能&#xff08;AI&#xff09;驱动的机器人&#xff0c;正在我们的生活中扮演着越来越重要的角色&#xff0c;而在化学合成实验室内&#xff0c;它们也在悄然改变着传统实验方式。 如今&#xff0c;科学家们在智能化学领域取得了新突破—— 来自英国利物浦大学的研究团…...

源代码泄漏怎么办?SDC沙盒成为破局利器

在数字化时代&#xff0c;源代码安全已成为企业关注的焦点。源代码的泄露不仅可能导致知识产权的损失&#xff0c;还可能被竞争对手利用&#xff0c;给企业带来巨大的经济损失和法律风险。因此&#xff0c;采取有效的源代码防泄漏措施至关重要。深信达的SDC沙盒防泄密软件&…...

【论文复现】基于图卷积网络的轻量化推荐模型

本文所涉及所有资源均在这里可获取。 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐、摄影的一位博主。 &#x1f4d7;本文收录于论文复现系列&#xff0c;大家有兴趣的可以看一看…...

使用ssh-key免密登录服务器或免密连接git代码仓库网站

ssh登录服务器场景 假设有两台机器&#xff0c;分别是&#xff1a; 源机器&#xff1a;主机A&#xff08;hostA&#xff09;&#xff0c;ip&#xff1a;198.168.0.1 目标机器&#xff1a;主机B&#xff08;hostB&#xff09;&#xff0c;ip&#xff1a;192.168.0.2 ssh-key免…...

自由学习记录(19)

unity核心也算是看完了吧&#xff0c;但觉得的确是少了点东西&#xff0c;之后再看mvc框架&#xff0c;和网络开发&#xff0c;&#xff0c;感觉有必要想想主次顺序了&#xff0c;毕竟在明年的3月之前尽量让自己更有贴合需求的能力 先了解一些相关概念&#xff0c;不用看懂&am…...

Elasticsearch中时间字段格式用法详解

Elasticsearch中时间字段格式用法详解 攻城狮Jozz关注IP属地: 北京 2024.03.18 16:27:51字数 758阅读 2,571 Elasticsearch&#xff08;简称ES&#xff09;是一个基于Lucene构建的开源、分布式、RESTful搜索引擎。它提供了全文搜索、结构化搜索以及分析等功能&#xff0c;广泛…...

蓝桥杯-网络安全比赛题目-遗漏的压缩包

小蓝同学给你发来了他自己开发的网站链接&#xff0c; 他说他故意留下了一个压缩包文件&#xff0c;里面有网站的源代码&#xff0c; 他想考验一下你的网络安全技能。 &#xff08;点击“下发赛题”后&#xff0c;你将得到一个http链接。如果该链接自动跳转到https&#xff0c;…...

ES海量数据插入如何优化性能?

2024年10月NJSD技术盛典暨第十届NJSD软件开发者大会、第八届IAS互联网架构大会在南京召开。百度文心快码总经理臧志分享了《AI原生研发新范式的实践与思考》&#xff0c;探讨了大模型赋能下的研发变革及如何在公司和行业中落地&#xff0c;AI原生研发新范式的内涵和推动经验。 …...

【独家首发】Midjourney 6.6+新增--depth-map指令实战手册:从单通道灰度图到可编辑景深层次(含Blender预处理模板)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Midjourney景深效果控制 景深&#xff08;Depth of Field&#xff09;是图像中清晰区域与虚化区域的过渡表现&#xff0c;在 Midjourney 中虽无原生 DSLR 式光圈参数&#xff0c;但可通过提示词工程、版本特性…...

2026年实用降AIGC软件:亲测AI率从90%降至4%的靠谱方案

一、前言&#xff1a;2026年毕业必过AIGC检测门槛 2026年国内高校对学术论文的AIGC疑似度审核全面收紧&#xff0c;绝大多数院校都发布了明确的AIGC检测数值要求&#xff1a;985、211院校规定本科论文AI率需低于20%&#xff0c;硕士论文AI率不得高于15%&#xff0c;普通高校也普…...

淘金币自动化脚本终极指南:每天节省20分钟,让淘宝任务自动完成

淘金币自动化脚本终极指南&#xff1a;每天节省20分钟&#xff0c;让淘宝任务自动完成 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本&#xff0c;包含蚂蚁森林收取能量&#xff0c;芭芭农场全任务&#xff0c;解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta…...

如何优化NovelReader性能:10个技巧降低内存使用率

如何优化NovelReader性能&#xff1a;10个技巧降低内存使用率 【免费下载链接】NovelReader 仿照"任阅"的追书、看书的小说阅读器。重写"任阅"的代码&#xff0c;优化代码逻辑和代码结构&#xff0c;降低内存使用率。重写小说阅读器&#xff0c;支持网络阅…...

Azure 资源管理器编程:resourcemanager 模块的 100+ 服务集成

Azure 资源管理器编程&#xff1a;resourcemanager 模块的 100 服务集成 【免费下载链接】azure-sdk-for-go This repository is for active development of the Azure SDK for Go. For consumers of the SDK we recommend visiting our public developer docs at: 项目地址:…...

安卓APP通过JNI调用ATSHA204A加密芯片实战指南

1. 项目概述与核心需求解析 在安卓应用开发领域&#xff0c;尤其是涉及物联网、金融支付、版权保护等高安全要求的场景&#xff0c;单纯依靠软件层面的加密算法已经不足以应对日益复杂的攻击手段。硬件加密芯片&#xff0c;如ATSHA204A&#xff0c;以其物理隔离、密钥不可读取等…...

告别实车测试!手把手教你用Vector VT平台搭建OBC/DCDC的HIL测试环境(附避坑指南)

新能源汽车OBC/DCDC控制器HIL测试环境搭建实战指南 在新能源汽车三电系统开发中&#xff0c;车载充电机(OBC)和DC/DC变换器的功能验证一直是工程师面临的挑战。传统实车测试不仅成本高昂&#xff0c;而且难以覆盖所有边界条件。硬件在环(HIL)测试技术通过将真实控制器接入虚拟车…...

RollBack RX Professional 快照管理避坑指南:锁定、任务属性设置与常见误区解析

RollBack RX Professional 快照管理避坑指南&#xff1a;锁定、任务属性设置与常见误区解析 在系统维护和数据安全领域&#xff0c;快照技术已经成为保障业务连续性的重要手段。RollBack RX Professional作为一款专业的系统还原工具&#xff0c;其快照管理功能在实际应用中展现…...

别再只会真彩色了!用ENVI玩转波段组合:揭秘植被红、水体蓝背后的遥感密码

遥感图像解译的艺术&#xff1a;ENVI波段组合背后的科学密码 当一张卫星遥感图像首次展现在眼前时&#xff0c;未经训练的眼睛往往只能看到一片模糊的色块。然而&#xff0c;对于掌握波段组合奥秘的解译专家来说&#xff0c;这些色彩背后隐藏着丰富的地表信息——健康的植被、水…...

IT工程/项目计划概要~项目结束表(模版)

项目计划概要Ⅰ&#xff09;项目启动&#xff08;PROJECT INITIATION&#xff09;1.EXCO(Executive Committee)审批2.已确认的意向书(Consent Letter)3.预风险评估4.合同(Contract)签署确认5.行业合规(Compliance)文档6.项目启动表7.项目章程签署确认Ⅱ&#xff09;项目计划8.业…...