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

基于cornerstone3D的dicom影像浏览器 第二十九章 自定义菜单组件

文章目录

  • 前言
  • 一、程序结构
    • 1. 菜单数据结构
    • 2. XMenu.vue
    • 3. XSubMenu.vue
    • 4. XSubMenuSlot.vue
    • 5. XMenuItem.vue
  • 二、调用流程
  • 总结


前言

菜单用于组织程序功能,为用户提供导航。是用户与程序交互非常重要的接口。
开源组件库像Element Plus和Ant Design中都提供了功能强大,使用方便的菜单组件 。
本章提供一个自定义菜单组件,核心思想是调用者提供菜单数据和显示位置,就能在指定位置显示出菜单组件。
效果如下:
在这里插入图片描述


一、程序结构

一共四个组件:
XMenu.vue 主菜单
XSubMenu.vue 递归子菜单
XSubMenuSlot.vue 子菜单与菜单项插槽
XMenuItem.vue 菜单项

1. 菜单数据结构

id: String,必填
text: String,菜单项文字,必填
icon: String,菜单项最左边图标,选填
checkable: Boolean,菜单项最左边是否显示选中图标 ✓ 与check共同作用, 选填
check: Boolean,是否选中,选填
image: String,菜单项文字区图片url,选填
separate: String,菜单项分割线,值:bottom/up/both,选填
subMenu: Array,子菜单数据,选填

示例:

let menuList = ref([{id: "view",text: "视图",icon: "",checkable: true,checked: true,separate: "bottom"},{id: "edit",text: "编辑文字",icon: "icon-jawbone",},{id: "pseudo",text: "伪彩1",icon: "",image: require("@/assets/images/hot_h1.png"),},{id: "file",text: "文件",icon: "",subMenu: [{id: "open",text: "打开",icon: "",separate: "bottom",},{id: "save",text: "保存刚才的工作",icon: "icon-lungs-line",},{id: "close",text: "关闭",icon: "",},{id: "menuitem1",text: "菜单项1",icon: "",},{id: "menuitem2",text: "菜单项2",icon: "",},{id: "menuitem3",text: "菜单项3",icon: "",},],},]);

显示效果:
在这里插入图片描述

2. XMenu.vue

用户入口

  1. 接受菜单数据和菜单位置
  2. 发送菜单项点击事件menuclick
  3. 自动计算菜单高度和宽度
  4. 菜单展开/收缩动画
<script lang="js" setup name="XMenu">
import { ref, computed, onMounted, reactive, h } from "vue";
import XSubMenu from "./XSubMenu.vue";
import XMenuItem from "./XMenuItem.vue";const emit = defineEmits(["menuclick"]);
let menuList = ref([]);const xMenu = ref(null);
const menuPos = reactive({ left: 0, top: 0 });
const ItemH = 32;
const fontSize = ref(14);
const showMenu = ref(false);const show = (menu, pos) => {menuList.value = menu;calcMenuPos(pos);showMenu.value = true;
}const calcMenuPos = (pos) => {const maxHeight = document.body.clientHeight;const maxWidth = document.body.clientWidth;if (pos.left + menuWidth.value > maxWidth) {menuPos.left = maxWidth - menuWidth.value;} else {menuPos.left = pos.left;}if (pos.top + menuHeight.value > maxHeight) {menuPos.top = pos.top2 - menuHeight.value;} else {menuPos.top = pos.top;}}const hide = () => {showMenu.value = false;
}const getRect = () => {return xMenu.value.getBoundingClientRect();
}const menuWidth = computed(() => {let w = 80;menuList.value.forEach((menu) => {const menuW = getMenuWidth(menu, fontSize.value);w = menuW > w ? menuW : w;});const maxW = document.body.clientWidth / 2;w += 40;const ret = w > maxW ? maxW : w;return ret;
});const menuHeight = computed(() => {let h = ItemH * menuList.value.length;return h;
});const menuStyle = computed(() => {return {width: menuWidth.value + "px",left: menuPos.left + "px",top: menuPos.top + "px",};
});const getMenuWidth = (menu, fontSize) => {const el = document.createElement("span");const text = menu.text;el.innerText = text;el.style.fontSize = fontSize + "px";el.style.position = "absolute";document.body.appendChild(el);let w = el.offsetWidth + 50;if (menu.image) {w += 100;}document.body.removeChild(el);return w;
}const hasChild = (menu) => {return menu.subMenu && menu.subMenu.length > 0;
}const onMenuClick = (menu) => {if (menu.checkable && menu.checked !== undefined) {menu.checked = !menu.checked;}hide();emit("menuclick", menu);
}const transBeforeEnter = (el) => {el.style.height = "0px";el.style.overflow = "hidden";
}
const transEnter = (el) => {el.style.height = "auto";const h = el.offsetHeight;el.style.height = "0px";requestAnimationFrame(() => {el.style.height = h + "px";el.style.transition = ".4s";});
}const transAfterEnter = (el) => {el.style.transition = "initial";el.style.overflow = null;
}const transBeforeLeave = (el) => {el.style.overflow = "hidden";el.style.transition = ".2s";
}
const transLeave = (el) => {el.style.height = "0px";
}
const transAfterLeave = (el) => {
}defineExpose({show,hide,getRect
});</script><template><!-- <Teleport to="body"> --><Transition@beforeEnter="transBeforeEnter"@enter="transEnter"@afterEnter="transAfterEnter"@before-leave="transBeforeLeave"@leave="transLeave"><ul class="x-menu" ref="xMenu" v-show="showMenu" :style="menuStyle"><template v-for="(item, index) in menuList"><XSubMenu v-if="hasChild(item)" :key="item.id" :menu="item" :index="index" @menuclick="onMenuClick" /><XMenuItem v-else :menu="item" @click="onMenuClick(item)" /></template></ul></Transition><!-- </Teleport> -->
</template><style lang="scss" scoped>
.x-menu {position: absolute;background-color: var(--color-theme-bg);color: var(--color-theme-text);border: 1px solid #aaa;z-index: 9999;
}
</style>

3. XSubMenu.vue

<script lang="js" setup name="XSubMenu">
import { ref, computed, onMounted } from "vue";
import XSubMenuSlot from "./XSubMenuSlot.vue";
import XMenuItem from "./XMenuItem.vue";const emit = defineEmits(["menuclick"]);const props = defineProps({menu: {type: Object,required: true},index: {type: Number,required: true}
});const hasChild = (menu) => {return menu.subMenu && menu.subMenu.length > 0;
};const onMenuClick = (menu) => {emit("menuclick", menu);
};
</script><template><XSubMenuSlot :menu="menu" :index="index"><template #title>{{ menu.text }}</template><ul><template v-for="(item, index) in menu.subMenu"><XSubMenu v-if="hasChild(item)" :key="item.id" :menu="item" @menuclick="onMenuClick" /><XMenuItem v-else :key="item.id" :menu="item" @click="onMenuClick(item)" /></template></ul></XSubMenuSlot>
</template><style lang="scss" scoped></style>

4. XSubMenuSlot.vue

两个插槽,分别显示XMenuItem、XSubMenu
与XMenu类似:

  1. 计算子菜单宽度和高度
  2. 计算子菜单显示位置
  3. 子菜单展开/收缩动画
<script lang="js" setup name="XSubMenuSlot">
import { ref, computed, onMounted, reactive } from "vue";const props = defineProps({menu: {type: Object,required: true},index: {type: Number,required: true}
});const ItemH = 32;
const showMenu = ref(false);
const subMenuPos = reactive({left: 0,top: 0
});const fontSize = ref(14);
const subMenuWidth = computed(() => {let w = 80;props.menu.subMenu.forEach((item) => {const menuW = getMenuWidth(item, fontSize.value);w = menuW > w ? menuW : w;});const maxW = document.body.clientWidth / 2;// menu sidebar-icon width: 40pxw += 40;const ret = w > maxW ? maxW : w;console.log('menuWidth', ret);return ret;
});const subMenuHeight = computed(() => {const height = ItemH * props.menu.subMenu.length;return height;
});const getMenuWidth = (menu, fontSize) => {const el = document.createElement("span");const text = menu.text;el.innerText = text;el.style.fontSize = fontSize + "px";el.style.position = "absolute";document.body.appendChild(el);let w = el.offsetWidth + 50;if (menu.image) {w += 100;}document.body.removeChild(el);return w;
}const subitemStyle = computed(() => {if (props.menu.separate) {const border = {};switch (props.menu.separate) {case "top":border.borderTop = "1px solid #aaa";break;case "bottom":border.borderBottom = "1px solid #aaa";break;case "both":border.borderTop = "1px solid #aaa";border.borderBottom = "1px solid #aaa";break;}return border;}
});const subMenuStyle = computed(() => {return {left: subMenuPos.left + "px",top: subMenuPos.top + "px",width: subMenuWidth.value + "px"}
});const show = (e) => {showMenu.value = true;const el = e.currentTarget;calcSubMenuPos(el);
}const calcSubMenuPos = (el) => {const maxWidth = document.body.clientWidth;const maxHeight = document.body.clientHeight;const rect = el.getBoundingClientRect();const xEnd = rect.right + subMenuWidth.value;const yEnd = rect.y + props.index*ItemH + subMenuHeight.value;if (xEnd > maxWidth) {subMenuPos.left = 0 - subMenuWidth.value;} else {subMenuPos.left = rect.width;}if (yEnd > maxHeight) {subMenuPos.top = ItemH - subMenuHeight.value;} else {subMenuPos.top = 0;}}const hide = (e) => {showMenu.value = false;
}const transBeforeEnter = (el) => {el.style.height = "0px";el.style.overflow = "hidden";
}
const transEnter = (el) => {el.style.height = "auto";const h = el.offsetHeight;el.style.height = "0px";requestAnimationFrame(() => {el.style.height = h + "px";el.style.transition = ".4s";});
}const transAfterEnter = (el) => {el.style.transition = "initial";el.style.overflow = null;
}const transBeforeLeave = (el) => {el.style.overflow = "hidden";el.style.transition = ".2s";
}
const transLeave = (el) => {el.style.height = "0px";
}
const transAfterLeave = (el) => {
}
</script><template><div class="container" @mouseenter="show" @mouseleave="hide"><li class="subitem" :style="subitemStyle"><span class="subitem-bar"></span><span class="subitem-text"><slot name="title"></slot></span><div class="subitem-right"><span class="subitem-right-icon"></span></div></li><Transition@beforeEnter="transBeforeEnter"@enter="transEnter"@afterEnter="transAfterEnter"@before-leave="transBeforeLeave"@leave="transLeave"><div class="submenu" v-show="showMenu" :style="subMenuStyle"><slot></slot></div></Transition></div>
</template><style lang="scss" scoped>
.container {position: relative;
}
.submenu {position: absolute;border: 1px solid #aaa;
}.subitem {display: flex;flex-direction: row;width: 100%;height: 32px;background-color: #fdfdfd;background-color: var(--color-menu-bg);z-index: 9999;overflow: hidden;
}.subitem-bar {width: 38px;height: 100%;background-color: var(--color-menu-bar);fill: var(--color-theme-text);
}.subitem-text {flex: 1;height: 32px;line-height: 32px;font-size: 16px;padding-left: 10px;cursor: default;user-select: none;-webkit-user-select: none; /* Safari */-moz-user-select: none; /* Firefox */-ms-user-select: none; /* IE 10 and IE 11 */
}.subitem-right {width: 30px;height: 100%;padding-right: 6px;
}.subitem-right-icon {float: right;width: 16px;height: 32px;background: url(../../assets/images/arrow_right.png) no-repeat center center;background-size: 16px auto;
}.subitem:hover {background-color: lightblue;color: blue;border: 1px solid lightskyblue;
}
</style>

5. XMenuItem.vue

菜单项,显示icon, text, image, separate

<script lang="js" setup name="XMenuItem">
import { ref, computed, onMounted } from "vue";
import SvgIcon from "../SvgIcon.vue";const props = defineProps({menu: {type: Object,required: true}
});const emit = defineEmits(["menuclick"]);const menuItemStyle = computed(() => {if (props.menu.separate) {const border = {};switch (props.menu.separate) {case "top":border.borderTop = "1px solid #aaa";break;case "bottom":border.borderBottom = "1px solid #aaa";break;case "both":border.borderTop = "1px solid #aaa";border.borderBottom = "1px solid #aaa";break;}return border;}
});const iconStyle = computed(() => {if (props.menu.checkable) {return {backgroundImage: props.menu.checked? `url("src/assets/images/choose.png")`: "",};} else {return {backgroundImage: props.menu.icon,};}
});
</script><template><li class="menuitem" :style="menuItemStyle"><div class="menuitem-bar"><svg-icon v-if="menu.icon" class="menuitem-bar__icon" :icon="menu.icon" size="28px" /><div v-else class="menuitem-bar__icon" :style="iconStyle"></div></div><div class="menuitem-text">{{ menu.text }}</div><img v-if="menu.image" class="menuitem-image" :src="menu.image" /></li>
</template><style lang="scss" scoped>
.menuitem {display: flex;flex-direction: row;align-items: center;height: 32px;background-color: var(--color-menu-bg);z-index: 9999;overflow: hidden;
}.menuitem-bar {display: flex;flex-direction: row;align-items: center;justify-content: center;width: 38px;height: 100%;background-color: var(--color-menu-bar);
}.menuitem-bar__icon {width: 38px;height: 100%;background-repeat: no-repeat;background-position: center center;background-size: 24px auto;fill: var(--color-theme-text);
}.menuitem-text {line-height: 32px;font-size: 16px;padding-left: 10px;cursor: default;user-select: none;-webkit-user-select: none; /* Safari */-moz-user-select: none; /* Firefox */-ms-user-select: none; /* IE 10 and IE 11 */
}.menuitem-image {margin-left: 8px;
}.menuitem:hover {background-color: lightblue;color: blue;border: 1px solid lightskyblue;
}
</style>

二、调用流程

  1. 导入XMenu.vue
  2. 在模板中添加XMenu
  3. 绑定变量
  4. 定义菜单数据
  5. 调用show函数
import XMenu from "./XMenu/XMenu.vue";const xMenu = ref(null);const menuList = ref([{id: "view",text: "视图",icon: "",checkable: true,checked: true,separate: "bottom"},{id: "edit",text: "编辑文字",icon: "icon-jawbone",},{id: "pseudo",text: "伪彩1",icon: "",image: require("@/assets/images/hot_h1.png"),},{id: "file",text: "文件",icon: "",subMenu: [{id: "open",text: "打开",icon: "",separate: "bottom",},{id: "save",text: "保存刚才的工作",icon: "icon-lungs-line",},{id: "close",text: "关闭",icon: "",},{id: "menuitem1",text: "菜单项1",icon: "",},{id: "menuitem2",text: "菜单项2",icon: "",},{id: "menuitem3",text: "菜单项3",icon: "",},],},]);const showMenu = (e) => {xMenu.value.show(menuList.value, { left: e.clientX, top: e.clientY})
}const hideMenu = () => {xMenu.value.hide();
}<template><div class="toolbar"><XMenu ref="xMenu" @mouseleave="hideMenu" />...<div class="toolbar-row"><el-button @click="showMenu">菜单2</el-button><el-button @click="dcmtag">DICOM标签</el-button><el-button @click="mprvr">MPR+VR</el-button><el-button @click="showMenu">菜单3</el-button></div>...</div>
</template>

总结

本章实现自定义菜单组件,支持图标、分隔线、选中图标、文字区图片、子菜单、展开/收缩动画。如需要显示更复杂内容,可自行扩展XMenuItem.vue
调用方便,只需要提供菜单数据和菜单显示位置。

相关文章:

基于cornerstone3D的dicom影像浏览器 第二十九章 自定义菜单组件

文章目录 前言一、程序结构1. 菜单数据结构2. XMenu.vue3. XSubMenu.vue4. XSubMenuSlot.vue5. XMenuItem.vue 二、调用流程总结 前言 菜单用于组织程序功能&#xff0c;为用户提供导航。是用户与程序交互非常重要的接口。 开源组件库像Element Plus和Ant Design中都提供了功能…...

【Block总结】DBlock,结合膨胀空间注意模块(Di-SpAM)和频域模块Gated-FFN|即插即用|CVPR2025

论文信息 标题: DarkIR: Robust Low-Light Image Restoration 作者: Daniel Feijoo, Juan C. Benito, Alvaro Garcia, Marcos Conde 论文链接&#xff1a;https://arxiv.org/pdf/2412.13443 GitHub链接&#xff1a;https://github.com/cidautai/DarkIR 创新点 DarkIR提出了…...

【学习笔记】单例类模板

【学习笔记】单例类模板 一、单例类模板 以下为一个通用的单例模式框架&#xff0c;这种设计允许其他类通过继承Singleton模板类来轻松实现单例模式&#xff0c;而无需为每个类重复编写单例实现代码。 // 命名空间&#xff08;Namespace&#xff09; 和 模板&#xff08;Tem…...

字符串加密(华为OD)

题目描述 给你一串未加密的字符串str,通过对字符串的每一个字母进行改变来实现加密,加密方式是在每一个字母str[i]偏移特定数组元素a[i]的量,数组a前三位已经赋值:a[0]=1,a[1]=2,a[2]=4。当i>=3时,数组元素a[i]=a[i-1]+a[i-2]+a[i-3]。例如:原文 abcde 加密后 bdgkr,…...

口罩佩戴检测算法AI智能分析网关V4工厂/工业等多场景守护公共卫生安全

一、引言​ 在公共卫生安全日益受到重视的当下&#xff0c;口罩佩戴成为预防病毒传播、保障人员健康的重要措施。为了高效、精准地实现对人员口罩佩戴情况的监测&#xff0c;AI智能分析网关V4口罩检测方案应运而生。该方案依托先进的人工智能技术与强大的硬件性能&#xff0c;…...

Double/Debiased Machine Learning

独立同步分布的观测数据 { W i ( Y i , D i , X i ) ∣ i ∈ { 1 , . . . , n } } \{W_i(Y_i,D_i,X_i)| i\in \{1,...,n\}\} {Wi​(Yi​,Di​,Xi​)∣i∈{1,...,n}}&#xff0c;其中 Y i Y_i Yi​表示结果变量&#xff0c; D i D_i Di​表示因变量&#xff0c; X i X_i Xi​表…...

HarmonyOS Next 弹窗系列教程(4)

HarmonyOS Next 弹窗系列教程&#xff08;4&#xff09; 介绍 本章主要介绍和用户点击关联更加密切的菜单控制&#xff08;Menu&#xff09; 和 气泡提示&#xff08;Popup&#xff09; 它们出现显示弹窗出现的位置都是在用户点击屏幕的位置相关 菜单控制&#xff08;Menu&…...

【C】-递归

1、递归概念 递归&#xff08;Recursion&#xff09;是编程中一种重要的解决问题的方法&#xff0c;其核心思想是函数通过调用自身来解决规模更小的子问题&#xff0c;直到达到最小的、可以直接解决的基准情形&#xff08;Base Case&#xff09;。 核心&#xff1a;自己调用…...

飞马LiDAR500雷达数据预处理

0 引言 在使用飞马D2000无人机搭载LiDAR500进行作业完成后&#xff0c;需要对数据进行预处理&#xff0c;方便给内业人员开展点云分类等工作。在开始操作前&#xff0c;先了解一下使用的软硬件及整体流程。 0.1 外业测量设备 无人机&#xff1a;飞马D2000S激光模块&#xff…...

Kerberos面试内容整理-在 Linux/Windows 中的 Kerberos 实践

Windows 实践: 在Windows环境中,Kerberos 几乎是无形融合的。用户使用域账号登录计算机时,实际上就完成了Kerberos的AS认证并获取TGT;此后的资源访问(如共享文件夹、打印机、数据库等)都会自动使用Kerberos进行验证,而无需用户干预。Windows通过LSASS进程维护和缓存用户…...

在 Allegro PCB Editor 中取消(解除或删除)已创建的 **Module** 的操作指南

在 Allegro PCB Editor 中取消&#xff08;解除或删除&#xff09;已创建的 Module 有两种主要场景&#xff0c;操作也不同&#xff1a; &#x1f4cc; 场景一&#xff1a;仅想解除元件与 Module 的关联&#xff08;保留元件位置和布线&#xff0c;但可独立编辑&#xff09; …...

基于springboot的校园社团信息系统的设计与实现

其他源码获取可以看首页&#xff1a;代码老y 个人简介&#xff1a;专注于毕业设计项目定制开发&#xff1a;springbootvue系统&#xff0c;Java微信小程序&#xff0c;javaSSM系统等技术开发&#xff0c;并提供远程调试部署、代码讲解、文档指导、ppt制作等技术指导。源码获取&…...

nodejs里面的http模块介绍和使用

Node.js的http模块是构建在libuv库之上&#xff0c;以JavaScript接口形式暴露出来的核心模块之一&#xff0c;它允许开发者轻松地创建和管理HTTP服务器及客户端&#xff0c;进而实现网络应用的快速开发。此模块的设计理念围绕着事件驱动和非阻塞I/O模型&#xff0c;这些特性使N…...

mamba架构和transformer区别

Mamba 架构和 Transformer 架构存在多方面的区别&#xff0c;具体如下&#xff1a; 计算复杂度1 Transformer&#xff1a;自注意力机制的计算量会随着上下文长度的增加呈平方级增长&#xff0c;例如上下文增加 32 倍时&#xff0c;计算量可能增长 1000 倍&#xff0c;在处理长序…...

嵌入式鸿蒙开发环境搭建操作方法与实现

Linux环境搭建镜像下载链接: 链接:https://pan.baidu.com/s/1F2f8ED5V1KwLjyYzKVx2yQ 提取码:Leun vscode和Linux系统连接的详细过程1.下载Visual Studio Code...

在 Spring Boot 中使用 WebFilter:实现请求拦截、日志记录、跨域处理等通用逻辑!

&#x1f4a1; 前言 在开发 Web 应用时&#xff0c;我们经常需要对所有请求进行统一处理&#xff0c;例如&#xff1a; 记录请求日志实现跨域&#xff08;CORS&#xff09;接口权限控制请求参数预处理防止 XSS 攻击 这些功能如果都写在每个 Controller 或 Service 里&#x…...

CSS预处理器:Sass与Less的语法和特性(含实际案例)

Sass&#xff08;SCSS语法示例&#xff09; 1. 变量&#xff1a;统一管理颜色 // 定义变量 $primary-color: #1a237e; $success-color: #4caf50; $font-size-base: 16px;// 实际应用 body {color: $primary-color;font-size: $font-size-base; }.button {background: $succes…...

QT常用控件(1)

控件是构成QT的基础元素&#xff0c;例如Qwidget也是一个控件&#xff0c;提供了一个‘空’的矩形&#xff0c;我们可以往里面添加内容和处理用户输入&#xff0c;例如&#xff1a;按钮&#xff08;QpushButton&#xff09;&#xff0c;基础显示控件&#xff08;Lable&#xff…...

明基编程显示器终于有优惠了,程序员快来,错过等一年!

最近618的活动已经陆续开始了&#xff0c;好多人说这是买数码产品的好时候&#xff0c;作为一名资深程序员&#xff0c;我做了不少功课&#xff0c;决定给自己升级办公设备&#xff0c;入手明基 RD 系列的显示器&#xff0c;这是市面上首家专注于我们程序员痛点和需求的产品&am…...

【计算机网络】非阻塞IO——select实现多路转接

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a;【计算机网络】NAT、代理服务器、内网穿透、内网打洞、局域网中交换机 &#x1f516;流水不争&#xff0…...

Figma 中构建 Master Control Panel (MCP) 的完整设计方案

以下是在 Figma 中构建 Master Control Panel (MCP) 的完整设计方案&#xff0c;专为设计系统管理而优化&#xff1a; 一、MCP 核心功能架构 #mermaid-svg-iZAnYxyYU4BtpeaE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#merma…...

什么是权威解析服务器?权威解析服务器哪些作用?

域名系统&#xff08;DNS&#xff09;是互联网的核心基础设施之一&#xff0c;它将易于记忆的域名转换为计算机能够识别的IP地址。DNS服务器在这一过程中扮演着至关重要的角色&#xff0c;它们可以分为以下几种类型: 根DNS服务器 根DNS服务器位于DNS层级结构的最顶端&#xf…...

LeetCode--23.合并k个升序链表

解题思路&#xff1a; 1.获取信息&#xff1a; 给出了多个升序链表&#xff0c;要求合并成一个升序链表&#xff0c;返回首元结点 2.分析题目&#xff1a; 外面在21题的时候&#xff0c;讲了怎样合并两个升序链表为一个升序链表&#xff0c;不了解的&#xff0c;建议去看一下21…...

ComfyUI 工作流

目录 🧠 ComfyUI 是什么? ComfyUI 的特点 🔧 ComfyUI 工作流程(节点图) 📌 简单理解 如何安装? 🧠 ComfyUI 是什么? ComfyUI 是一个 Stable Diffusion 的图形化用户界面(GUI),专门用来生成图像。它通过 节点图(Node Graph)形式来让用户定义图像生成的每…...

使用glide 同步获取图片

在 Glide 中&#xff0c;可以使用asBitmap()方法来获取图片的Bitmap对象&#xff0c;进而同步地加载图片。以下是具体示例&#xff1a; String imageUrl "https://example.com/image.jpg"; Bitmap bitmap Glide.with(context).asBitmap().load(imageUrl).apply(ne…...

【推荐算法】NeuralCF:深度学习重构协同过滤的革命性突破

NeuralCF&#xff1a;深度学习重构协同过滤的革命性突破 一、算法背景知识&#xff1a;协同过滤的演进与局限1.1 协同过滤的发展历程1.2 传统矩阵分解的缺陷 二、算法理论/结构&#xff1a;NeuralCF架构设计2.1 基础NeuralCF结构2.2 双塔模型进阶结构2.3 模型实现流程对比 三、…...

负载均衡相关基本概念

负载均衡在系统架构设计中至关重要&#xff0c;其核心目标是合理分配负载&#xff0c;提升系统整体性能和可靠性。本文简要介绍了负载均衡的基本概念&#xff0c;包括四层和七层负载均衡、负载均衡的使用场景和实现方式、负载均衡的常用算法以及一些配置相关知识。 1、负载均衡…...

服务器中日志分析的作用都有哪些

服务器日志是用来检测和排查可疑行为的主要工具&#xff0c;运维团队可以通过分析和解读日志文件&#xff0c;发现服务器中潜在的网络安全威胁或异常活动&#xff0c;下面&#xff0c;就让小编和大家一起来了解一下服务器中日志分析的作用都有什么吧&#xff01; 对于服务器中的…...

【React】useId

在 React 中&#xff0c;useId 是 React 18 引入的一个 Hook&#xff0c;用于生成一个在组件生命周期中保持稳定的唯一 ID。它主要用于&#xff1a; 无障碍&#xff08;accessibility&#xff09;场景&#xff0c;比如表单控件需要一个 id 和 label 的 htmlFor 属性配对。服务…...

【51单片机】0. 基础软件安装

最近心血来潮对单片机感兴趣&#xff0c;想着以后工作不景气了就跳槽&#xff0c;兴趣使然为以后做打算吧&#xff0c;开始跟着江科大学习单片机 1. 需要安装的软件说明 使用到的软件包括&#xff1a; Keli&#xff1a;写嵌入式代码的 stc-isp&#xff1a;烧录&下载代码…...