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

【行业树选择器组件:基于Vue3与Element Plus的高性能树形选择组件优化与重构】

行业树选择器组件:基于Vue3与Element Plus的高性能树形选择组件优化与重构

组件概述与背景

行业树选择器是一个基于Element Plus的ElTreeSelect封装的业务组件,主要应用于能源管理系统中,用于展示和选择国标行业分类体系的四级层级结构。该组件位于项目的/src/views/energy-saving-management/key-energy-using/目录中,由多个文件协同工作实现。

功能定位与使用场景

该组件主要用于:

  • 重点用能单位表单中的行业分类选择
  • 能源管理档案中的行业数据展示与编辑
  • 企业分类统计与筛选

开发背景

在能源管理系统中,行业分类是重要的基础数据。国标行业分类采用四级结构(如A-农林牧渔业->A01-农业->A011-谷物种植->A0111-稻谷种植),系统需要一个能清晰展示此层级结构并支持各级选择的组件。

现有实现的不足

  1. 行业树展开问题 - 选择三级行业代码时不能自动展开到正确的层级结构
  2. 代码冗余 - 现有代码中存在大量重复逻辑和条件判断
  3. 错误处理不足 - 在数据加载失败或节点查找失败时缺乏有效的降级策略

技术定位

该组件属于业务组件,基于Element Plus的ElTreeSelect进行封装,并与系统中的行业数据API集成。

设计目标与原则

功能目标

  1. 支持四级行业分类树的展示与选择
  2. 实现行业代码输入与节点自动匹配
  3. 优化展开逻辑,确保选择任一级别的行业代码时都能正确展开其层级结构

性能目标

  1. 减少API调用次数,避免重复加载数据
  2. 优化树节点查找算法
  3. 使用缓存机制提高响应速度

可维护性目标

  1. 减少代码冗余,提高代码质量
  2. 增强错误处理能力
  3. 优化函数结构,明确职责分工

组件设计与API

在表单中的使用

组件在add-modal.vue中的使用方式:

<el-tree-selectv-else-if="item.type === 'tree-select' && item.prop === 'industry'":placeholder="item.rules[0].message"v-model="formData[item.prop]":data="industryTreeData":props="industryTreeProps"node-key="code"check-strictlyvalue-key="code"filterableclearable:default-expanded-keys="expandedKeys":render-after-expand="false"@visible-change="handleTreeVisibleChange"@change="handleIndustryChange"@node-click="handleIndustryNodeClick"
/>

核心数据结构

// 行业树节点数据结构
interface IndustryTreeNode {name: string;       // 行业名称code: string;       // 行业代码value: string;      // 选择值,通常与code相同isLeaf: boolean;    // 是否叶节点children?: IndustryTreeNode[]; // 子节点
}// 树属性定义
const industryTreeProps = {label: 'name',value: 'code',children: 'children',isLeaf: 'isLeaf'
};

组件内部状态

// 行业树数据
const industryTreeData = ref<any[]>([]);
// 当前选中节点
const industrySelectedNode = ref<any>(null);
// 展开的节点键值
const expandedKeys = ref<string[]>([]);
// 树数据缓存
const treeDataCache = ref<any[]>([]);

实现细节与优化对比

1. 行业树初始化

优化前:

// 初始化行业树
async function initIndustryTree() {try {const selectedIndustryCode = formData.value.industry;// 如果已有缓存数据,直接使用if (treeDataCache.value.length > 0) {industryTreeData.value = treeDataCache.value;if (selectedIndustryCode && typeof selectedIndustryCode === 'string') {// 更新展开路径和选中节点updateTreeSelection(selectedIndustryCode);}return;}// 无缓存则加载数据if (selectedIndustryCode && typeof selectedIndustryCode === 'string') {await loadTreeForCode(selectedIndustryCode);return;}const treeData = await loadFullIndustryTree();industryTreeData.value = treeData;treeDataCache.value = treeData; // 缓存数据} catch (error) {console.error('初始化行业树失败', error);industryTreeData.value = [];}
}

优化思路:
此函数主要逻辑是处理行业树的初始化和缓存,不需要进行大幅度修改,但可以抽取重复的数据加载和缓存逻辑到单独的函数中,提高代码可读性。

优化后:

// 加载并缓存树数据
const loadAndCacheTree = async () => {const treeData = await loadFullIndustryTree();industryTreeData.value = treeData;treeDataCache.value = treeData;
};// 初始化行业树
const initIndustryTree = async () => {try {const selectedCode = formData.value.industry;// 有缓存数据时直接使用if (treeDataCache.value.length > 0) {industryTreeData.value = treeDataCache.value;selectedCode && typeof selectedCode === 'string' && updateTreeSelection(selectedCode);return;}// 无缓存时加载数据selectedCode && typeof selectedCode === 'string' ? await initLoadTree(selectedCode) : loadAndCacheTree();} catch (error) {console.error('初始化行业树失败', error);industryTreeData.value = [];}
};

2. 更新树选择状态

优化前:

// 更新树的选中状态和展开路径
function updateTreeSelection(industryCode: string) {if (!industryCode || !treeDataCache.value.length) return;// 获取展开路径const nodePath = getNodePath(treeDataCache.value, industryCode);expandedKeys.value = nodePath;// 更新选中节点updateSelectedIndustryNode(treeDataCache.value, industryCode);
}

优化思路:
该函数逻辑简洁明了,主要关注点应该是getNodePath函数的实现。可以通过添加日志输出增强代码可调试性。

优化后:

// 更新树的选中状态和展开路径
function updateTreeSelection(industryCode: string) {if (!industryCode || !treeDataCache.value.length) return;console.log('更新树选择状态,行业代码:', industryCode);// 获取展开路径const nodePath = getNodePath(treeDataCache.value, industryCode);console.log('获取展开路径结果:', nodePath);if (nodePath && nodePath.length > 0) {// 确保展开路径中的代码都是字符串expandedKeys.value = nodePath.map(code => String(code));console.log('设置展开键值:', expandedKeys.value);} else {console.warn('未能获取展开路径,可能无法正确展示树形结构');}// 更新选中节点updateSelectedIndustryNode(treeDataCache.value, industryCode);
}

3. 加载特定行业代码的树

优化前:

// 加载指定行业代码的节点并设置展开路径
async function loadTreeForCode(industryCode: string) {if (!industryCode) return;try {// 加载完整树数据const treeData = await loadFullIndustryTree();industryTreeData.value = treeData;treeDataCache.value = treeData; // 缓存数据// 更新展开路径和选中节点updateTreeSelection(industryCode);} catch (error) {console.error('加载行业树数据失败:', error);formData.value.industry = industryCode;}
}

优化思路:
此函数可以重用刚才定义的loadAndCacheTree函数,减少代码重复。

优化后:

// 加载指定行业代码的树
const initLoadTree = async (industryCode: string) => {if (!industryCode) return;try {console.log('加载行业树数据,代码:', industryCode);await loadAndCacheTree();updateTreeSelection(industryCode);} catch (error) {console.error('加载行业树数据失败:', error);formData.value.industry = industryCode;}
};

4. 节点路径查找算法

优化前:

// 获取节点的路径,返回节点code数组
export const getNodePath = (tree: any[], code: string): string[] => {if (!code || !tree || !Array.isArray(tree) || tree.length === 0) return [];// 进行前处理,确定行业代码的层级let targetLevel = 0;// 分析行业代码的层级结构if (code.length === 1) {// 一级行业代码,如 'A','B'targetLevel = 1;} else if (code.length === 3) {// 二级行业代码,如 'A01', 'B06'targetLevel = 2;} else if (code.length === 4) {// 三级行业代码,如 'A011', 'C171'targetLevel = 3;} else if (code.length === 5) {// 四级行业代码,如 'C1711'targetLevel = 4;}console.log('目标代码层级:', targetLevel, '代码:', code);// 用于存储路径的数组const path: string[] = [];// 如果是多级代码,先尝试找到父级节点if (targetLevel > 1) {// 获取一级行业代码const firstLevelCode = code.charAt(0);// 查找一级节点const firstLevelNode = tree.find((node) => node.code === firstLevelCode);if (firstLevelNode) {// 添加一级节点到路径path.push(firstLevelCode);if (targetLevel > 2 && firstLevelNode.children) {  // 这里有问题// 获取二级行业代码const secondLevelCode = code.substring(0, 3);// 查找二级节点const secondLevelNode = firstLevelNode.children.find((node) => node.code === secondLevelCode);if (secondLevelNode) {// 添加二级节点到路径path.push(secondLevelCode);if (targetLevel > 3 && secondLevelNode.children) {// 获取三级行业代码const thirdLevelCode = code.substring(0, 4);// 查找三级节点const thirdLevelNode = secondLevelNode.children.find((node) => node.code === thirdLevelCode);if (thirdLevelNode) {// 添加三级节点到路径path.push(thirdLevelCode);// 如果是四级代码,则添加完整代码if (targetLevel === 4) {path.push(code);}}}}}}}// 如果通过层级分析没有找到路径,再尝试递归查找if (path.length === 0) {// 尝试精确匹配查找路径const resultPath: string[] = [];const foundExact = findPathRecursive(tree, code, [], resultPath, true);if (foundExact) {console.log('通过递归找到精确匹配的节点路径:', resultPath);return resultPath;}// 如果精确匹配失败,尝试模糊匹配const fuzzyResultPath: string[] = [];const foundFuzzy = findPathRecursive(tree, code, [], fuzzyResultPath, false);if (foundFuzzy) {console.log('通过递归找到模糊匹配的节点路径:', fuzzyResultPath);return fuzzyResultPath;}} else {console.log('通过层级分析找到节点路径:', path);}// 如果仍然没有找到路径,尝试从目标代码本身构建路径if (path.length === 0 && targetLevel > 1) {console.log('尝试从目标代码本身构建路径');// 一级代码const level1Code = code.charAt(0);path.push(level1Code);// 如果是二级以上代码if (targetLevel >= 2) {const level2Code = code.substring(0, 3);path.push(level2Code);}// 如果是三级以上代码if (targetLevel >= 3) {const level3Code = code.substring(0, 4);path.push(level3Code);}// 如果是四级代码if (targetLevel === 4) {path.push(code);}console.log('从代码本身构建的路径:', path);}return path;
};

优化思路:
这个函数是整个组件的核心,包含了多种查找策略。注意到这里存在一个逻辑错误:对于二级行业代码,条件判断 targetLevel > 2 导致无法进入处理二级代码的分支。我们需要修改条件判断,并将函数重构为更小、更专注的部分。

优化后:

// 获取节点的路径,返回节点code数组
export const getNodePath = (tree: any[], code: string): string[] => {if (!code || !tree?.length) return [];// 确定行业代码的层级const targetLevel = getCodeLevel(code);console.log('目标代码层级:', targetLevel, '代码:', code);// 通过层级分析构建路径const path = buildPathByLevel(tree, code, targetLevel);// 如果层级分析成功找到路径if (path.length > 0) {console.log('通过层级分析找到节点路径:', path);return path;}// 尝试精确匹配查找路径const resultPath: string[] = [];if (findPathRecursive(tree, code, [], resultPath, true)) {console.log('通过递归找到精确匹配的节点路径:', resultPath);return resultPath;}// 如果精确匹配失败,尝试模糊匹配const fuzzyResultPath: string[] = [];if (findPathRecursive(tree, code, [], fuzzyResultPath, false)) {console.log('通过递归找到模糊匹配的节点路径:', fuzzyResultPath);return fuzzyResultPath;}// 所有方法都失败,从代码本身构建路径if (targetLevel > 1) {const fallbackPath = buildFallbackPath(code, targetLevel);console.log('从代码本身构建的路径:', fallbackPath);return fallbackPath;}return [];
};// 根据代码长度确定层级
function getCodeLevel(code: string): number {const codeLength = code.length;if (codeLength === 1) return 1;       // 一级行业代码,如 'A','B'if (codeLength === 3) return 2;       // 二级行业代码,如 'A01', 'B06'if (codeLength === 4) return 3;       // 三级行业代码,如 'A011', 'C171'if (codeLength === 5) return 4;       // 四级行业代码,如 'C1711'return 0;
}// 根据代码层级构建路径
function buildPathByLevel(tree: any[], code: string, targetLevel: number): string[] {const path: string[] = [];if (targetLevel <= 1) return path;// 一级代码const firstLevelCode = code.charAt(0);const firstLevelNode = tree.find(node => node.code === firstLevelCode);if (!firstLevelNode) return path;path.push(firstLevelCode);// 二级代码 - 关键修改if (targetLevel > 1 && firstLevelNode.children?.length) {  // 修改条件判断const secondLevelCode = code.substring(0, 3);const secondLevelNode = firstLevelNode.children.find(node => node.code === secondLevelCode);if (!secondLevelNode) return path;path.push(secondLevelCode);// 三级代码if (targetLevel > 2 && secondLevelNode.children?.length) {const thirdLevelCode = code.substring(0, 4);const thirdLevelNode = secondLevelNode.children.find(node => node.code === thirdLevelCode);if (!thirdLevelNode) return path;path.push(thirdLevelCode);// 四级代码if (targetLevel > 3) {path.push(code);}}}return path;
}// 构建后备路径(当树中找不到节点时)
function buildFallbackPath(code: string, targetLevel: number): string[] {const path: string[] = [];// 一级代码path.push(code.charAt(0));// 二级及以上代码if (targetLevel >= 2) path.push(code.substring(0, 3));// 三级及以上代码if (targetLevel >= 3) path.push(code.substring(0, 4));// 四级代码if (targetLevel === 4) path.push(code);return path;
}

5. 递归查找路径

优化前:

// 递归查找节点路径的辅助函数
const findPathRecursive = (nodes: any[],targetCode: string,currentPath: string[],resultPath: string[],exactMatch: boolean
): boolean => {if (!nodes || !Array.isArray(nodes)) return false;for (const node of nodes) {// 复制当前路径,添加当前节点const tempPath = [...currentPath, node.code];// 检查是否找到目标节点let isTarget = false;if (exactMatch) {// 精确匹配isTarget = node.code === targetCode;} else {// 模糊匹配isTarget =(node.code && targetCode.includes(node.code)) ||(node.code && node.code.includes(targetCode));}// 如果找到目标节点,保存路径并返回成功if (isTarget) {resultPath.push(...tempPath);return true;}// 如果有子节点,递归查找if (node.children && Array.isArray(node.children) && node.children.length > 0) {if (findPathRecursive(node.children, targetCode, tempPath, resultPath, exactMatch)) {return true;}}}return false;
};

优化思路:
简化条件判断,使用更简洁的代码风格。

优化后:

// 递归查找节点路径的辅助函数
const findPathRecursive = (nodes: any[],targetCode: string,currentPath: string[],resultPath: string[],exactMatch: boolean
): boolean => {if (!nodes?.length) return false;for (const node of nodes) {// 添加当前节点到路径const tempPath = [...currentPath, node.code];// 检查是否找到目标节点const isTarget = exactMatch ? node.code === targetCode  // 精确匹配: (node.code && targetCode.includes(node.code)) || (node.code && node.code.includes(targetCode)); // 模糊匹配// 找到目标节点,保存路径并返回成功if (isTarget) {resultPath.push(...tempPath);return true;}// 递归查找子节点if (node.children?.length && findPathRecursive(node.children, targetCode, tempPath, resultPath, exactMatch)) {return true;}}return false;
};

6. 处理树选择器可见性变化

优化前:

// 处理树选择器可见性变化
function handleTreeVisibleChange(visible: boolean) {if (visible && industryTreeData.value.length === 0) {// 仅在首次显示且无数据时加载initIndustryTree();}
}

优化思路:
使用短路求值简化代码。

优化后:

// 处理树选择器可见性变化
const handleTreeVisibleChange = (visible: boolean) => {visible && industryTreeData.value.length === 0 && initIndustryTree();
};

7. 处理行业选择变更

优化前:

// 处理行业树选择变更
function handleIndustryChange(val: any) {console.log('行业选择变更:', val);if (val && typeof val === 'object') {formData.value.industry = val.code || '';industrySelectedNode.value = val;} else if (typeof val === 'string' && val) {formData.value.industry = val;updateIndustrySelectedNodeByCode(val);} else {formData.value.industry = '';industrySelectedNode.value = null;console.log('清空行业选择');}
}

优化思路:
添加自动更新树选择状态的逻辑,确保选择后能正确展开节点。

优化后:

// 处理行业树选择变更
const handleIndustryChange = (val: any) => {console.log('行业选择变更:', val);if (val && typeof val === 'object') {formData.value.industry = val.code || '';industrySelectedNode.value = val;// 如果选择了节点,确保展开到该节点if (val.code) {updateTreeSelection(val.code);}} else if (typeof val === 'string' && val) {formData.value.industry = val;updateIndustrySelectedNodeByCode(val);// 确保展开到该节点updateTreeSelection(val);} else {formData.value.industry = '';industrySelectedNode.value = null;expandedKeys.value = [];}
};

8. 根据行业代码更新选中节点

优化前:

// 根据行业代码更新选中节点
function updateIndustrySelectedNodeByCode(industryCode: string) {if (industryTreeData.value && industryTreeData.value.length > 0) {const node = findNodeByCode(industryTreeData.value, industryCode);if (node) {industrySelectedNode.value = node;console.log('根据代码找到并选中节点:', node);} else {industrySelectedNode.value = { code: industryCode };console.log('未找到节点,保存代码:', industryCode);}} else {industrySelectedNode.value = { code: industryCode };}
}

优化思路:
简化逻辑,减少条件判断和重复代码。

优化后:

// 根据行业代码更新选中节点
const updateIndustrySelectedNodeByCode = (industryCode: string) => {if (!industryTreeData.value?.length) {industrySelectedNode.value = { code: industryCode };return;}const node = findNodeByCode(industryTreeData.value, industryCode);industrySelectedNode.value = node || { code: industryCode };
};

9. 处理行业节点点击事件

优化前:

// 处理行业节点点击事件
function handleIndustryNodeClick(data: any, node: any) {console.log('行业节点点击:', data, node);if (data && data.code) {industrySelectedNode.value = data;}
}

优化思路:
使用可选链和短路求值简化代码。

优化后:

// 处理行业节点点击事件
const handleIndustryNodeClick = (data: any) => {data?.code && (industrySelectedNode.value = data);
};

核心问题修复

整个优化过程中,解决的最关键问题是修正了getNodePath函数中的条件判断错误:

优化前:

if (targetLevel > 2 && firstLevelNode.children) {// 获取二级行业代码// ...
}

优化后:

if (targetLevel > 1 && firstLevelNode.children?.length) {// 获取二级行业代码// ...
}

这个看似微小的修改解决了选择三级行业代码不能自动展开到正确层级结构的问题。原来的条件targetLevel > 2意味着只有当目标是三级或四级代码时才会处理二级代码的查找,这导致对于二级代码自身无法正确构建路径。

修改为targetLevel > 1后,只要目标是二级及以上代码,就会尝试查找和构建二级路径,从而正确展开树形结构。

优化与性能

主要优化点

  1. 缓存机制:使用treeDataCache缓存树数据,避免重复加载

    // 如果已有缓存数据,直接使用
    if (treeDataCache.value.length > 0) {industryTreeData.value = treeDataCache.value;// ...return;
    }
    
  2. 懒加载策略:只在实际需要时才加载行业树数据

    // 仅在首次显示且无数据时加载
    if (visible && industryTreeData.value.length === 0) {initIndustryTree();
    }
    
  3. 多策略路径查找:采用层级分析、精确匹配、模糊匹配和后备构建等多种策略查找节点路径

  4. 展开路径优化:修复了选择三级行业代码不能自动展开的问题

问题解决

对于选择三级行业代码不能自动展开的问题,主要通过以下改进解决:

  1. 修正了路径查找逻辑中的条件判断:

    // 原代码
    if (targetLevel > 2 && firstLevelNode.children) {// ...
    }// 修改为
    if (targetLevel > 1 && firstLevelNode.children) {// ...
    }
    
  2. 增强了路径查找失败时的后备策略,确保总能生成合理的展开路径

应用案例

在表单中的应用

在重点用能单位表单(add-modal.vue)中,该组件用于选择企业所属行业:

<el-form-item :label="item.label" :prop="item.prop" :rules="item.rules"><!-- 行业树形选择器 --><el-tree-selectv-else-if="item.type === 'tree-select' && item.prop === 'industry'":placeholder="item.rules[0].message"v-model="formData[item.prop]":data="industryTreeData":props="industryTreeProps"node-key="code"check-strictlyvalue-key="code"filterableclearable:default-expanded-keys="expandedKeys":render-after-expand="false"@visible-change="handleTreeVisibleChange"@change="handleIndustryChange"@node-click="handleIndustryNodeClick"/>
</el-form-item>

详情查看模式

在详情查看模式下,组件以只读方式展示所选行业:

// 加载行业数据(用于详情)
const loadIndustryDataForDetail = async () => {if ((props.type === viewType.EDIT || props.type === viewType.DETAIL) &&formData.value.industry &&typeof formData.value.industry === 'string') {try {await initLoadTree(formData.value.industry);} catch (error) {console.error('加载行业节点失败', error);}}
};

学习与收获

技术决策考量

  1. 多策略路径查找:在节点查找中采用多种策略,从精确到模糊,再到后备构建,确保总能生成合理的展开路径
  2. 缓存数据提升性能:通过缓存树数据,减少不必要的API调用,提升响应速度
  3. 逐层降级策略:在处理节点查找失败时,采用逐层降级策略,确保系统稳健运行

遇到的挑战与解决方法

  1. 节点路径查找复杂性:行业代码具有特定的层级结构,需要根据代码长度和内容进行不同的处理

    • 解决方法:根据代码长度确定层级,采用多种查找策略并提供后备方案
  2. 条件判断繁杂:路径查找涉及多层嵌套条件

    • 解决方法:优化条件判断逻辑,确保关键条件判断准确无误

设计模式应用

  1. 策略模式:在节点路径查找中采用多种策略(层级分析、精确匹配、模糊匹配、后备构建)
  2. 缓存模式:使用树数据缓存提升性能
  3. 降级策略:在高精度策略失效时逐步采用低精度策略,确保系统稳定性

未来规划

待解决的问题

  1. 代码冗余:路径查找算法仍然包含较多嵌套逻辑,可进一步优化
  2. 类型定义不足:部分函数参数和返回值缺乏精确的TypeScript类型定义
  3. 错误提示不足:在节点查找失败时缺乏对用户的友好提示

计划中的优化

  1. 重构路径查找算法:进一步简化路径查找算法,提取重复逻辑
  2. 增强类型定义:完善TypeScript类型定义,提高代码质量
  3. 用户体验优化:添加节点查找失败时的友好提示

总结

行业树选择器组件的优化重构展示了如何通过细致的代码分析找出潜在问题,并通过精确的修改和重构提高代码质量。这个案例也说明了在前端开发中,有时微小的条件判断错误可能导致功能异常,而细心的分析和优化是解决复杂问题的关键。

我们通过以下几个方面改进了组件:

  1. 功能修复 - 解决了选择三级行业代码不能自动展开的问题
  2. 代码优化 - 简化了条件判断、抽取公共逻辑、减少代码冗余
  3. 性能提升 - 通过缓存和懒加载策略减少API调用和不必要的计算
  4. 健壮性增强 - 增加了多种降级策略,确保在各种情况下都能正常工作
  5. 可读性提高 - 重构复杂函数,使代码更加清晰易读

这次优化不仅解决了具体的功能问题,也展示了如何在实际项目中运用代码重构技术来提高组件质量。通过这个案例,我们可以看到,即使是看似微小的修改,也可能对功能产生重大影响,因此在开发和重构过程中,需要深入理解代码逻辑,仔细分析潜在影响。

最终,经过优化的行业树选择器组件不仅功能更加完善,也更易于维护和扩展,为系统提供了更好的用户体验。

相关文章:

【行业树选择器组件:基于Vue3与Element Plus的高性能树形选择组件优化与重构】

行业树选择器组件&#xff1a;基于Vue3与Element Plus的高性能树形选择组件优化与重构 组件概述与背景 行业树选择器是一个基于Element Plus的ElTreeSelect封装的业务组件&#xff0c;主要应用于能源管理系统中&#xff0c;用于展示和选择国标行业分类体系的四级层级结构。该…...

PasteForm框架开发之Entity多级嵌套的表单的实现

你相信么,使用PasteForm框架开发&#xff0c;管理端居然不要写代码&#xff01;&#xff01;&#xff01; 一起来看看PasteForm是否支持多级表模式(外表) 需求假设 假如有这么一个需求&#xff0c;就是订单表&#xff0c;包含了多级的信息&#xff0c;比如这个订单包含了哪些…...

Anaconda笔记

下载Anaconda 清华源 官方源 本文下载&#xff1a;Anaconda3-2024.10-1-Windows-x86_64.exe 建议不要安装到C盘&#xff0c;我的安装到D&#xff1a;Anaconda目录 设置环境变量 WinR cmd命令行输入&#xff1a; conda --version&#xff1a;可以查看到版本信息安装成功c…...

Linux——共享内存

目录 一、共享内存概念 二、共享内存的一些函数 2.1 shmget 创建共享内存 2.2 shmat 访问共享内存 2.3 shmdt 解除共享内存的映射 2.4 shnctl 删除共享内存段 三、共享内存 3.1 创建测试进程 3.2 使用循环测试 ​编辑 3.3 共享内存写入程序 3.4 带有信号量的共享内…...

计算机系统---烤机(性能测评)

计算机烤机 一、烤机的定义与核心目的 烤机&#xff08;Burn-in Test&#xff09; 是通过对计算机硬件施加持续高负载&#xff0c;模拟极端运行环境&#xff0c;以验证硬件稳定性、性能极限、散热能力及潜在缺陷的测试方法。核心目标包括&#xff1a; 硬件稳定性验证&#x…...

Linux命令+Git命令

Linux命令Git命令 linux查看两个操作系统cd命令的区别操作文件和文件夹vim不同模式保存和退出 Git linux Linux操作系统中&#xff0c;几乎所有的东西都以文件夹或文件形式存在&#xff0c;这些文件夹/文件有一个共同的根目录/。如果我们在某块磁盘A上&#xff08;无其他分区&…...

【前端】Nuxt打包部署的几种方式

一、总结知识点 Nuxt 是基于 Vue 的服务端渲染框架&#xff0c;部署方式主要取决于你使用的 Nuxt 模式&#xff1a;Universal (SSR)、SPA 或 Static Site Generation (SSG)。不同模式下的打包部署流程略有不同。以下将分别介绍 Nuxt 应用的打包和部署方式。 二、详细说明 1. …...

DP 16bit位宽数据扰码实现和仿真

DisplayPort 1.4协议中数据需进行扰码&#xff0c;扰码用到了16-bit LFSR&#xff0c;表达式如下。 LFSR每移位8个bit后&#xff0c;用最高有效 8 位以相反的位顺序与一个字节数据进行异或从而实现数据加扰/解扰。 我们已利用这个框图进行8个时钟周期迭代&#xff0c;得到了和…...

力扣每日打卡 1534. 统计好三元组 (简单)

力扣 1534. 统计好三元组 简单 前言一、题目内容二、解题方法1. 暴力解法2.官方题解2.1 方法一&#xff1a;枚举2.2 方法二&#xff1a;枚举优化 前言 这是刷算法题的第十二天&#xff0c;用到的语言是JS 题目&#xff1a;力扣 1534. 统计好三元组 (简单) 一、题目内容 给你一…...

CExercise_13_1排序算法_1插入排序

题目&#xff1a; 请自己手动实现插入排序算法&#xff1a; // 插入排序 void insertion_sort(int arr[], int len); 然后给定一个int数组&#xff0c;实现将它从小到大进行排序。 关键点 分析&#xff1a; 在插入排序中&#xff0c;稳定性指的是排序算法能够保持相等元素的原始…...

图论--DFS搜索图/树

目录 一、图的存储结构 二、题目练习 846. 树的重心 - AcWing题 dfs&#xff0c;之前学习的回溯算法好多都是用dfs实现搜索的&#xff08;把题目抽象成树形结构来搜索&#xff09;&#xff0c;其实 回溯算法就是 深搜&#xff0c;只不过针对某一搜索场景 我们给他一个更细分…...

blender 超逼真角色daz 纹理材质 humanpro插件

https://www.youtube.com/KhanhVo-zp9lh/featured https://superhivemarket.com/products/humanpro https://superhivemarket.com/products/humanpro HUMANPRO 插件 - BLENDER HumanPro 是一款专为帮助用户轻松快速地创建高度精细逼真的人体皮肤纹理和复杂皱纹而设计的插件…...

关于响应式编程框架ReactiveUI的感悟

1.我第一个接触的MVVM框架是Caliburn Micro&#xff0c;后来接触到了ReactiveUI这种响应式编程思想的框架&#xff0c;主要说一下本人目前学习感想&#xff08;针对尽快上手使用MVVM开发WPF&#xff09;&#xff1b; 首当其冲的工具是DeepSeek&#xff0c;总结好问题直接提问&…...

Swift —— delegate 设计模式

一、什么是 delegate 模式 所谓 delegate 就是代理模式。简单来说&#xff0c;delegate 模式就是在类的函数里运行完一段代码后&#xff0c;你可以通过一个符合某个代理协议的属性来调代理的方法。其中&#xff0c;代理方法就是回调函数。 二、delegate 模式与闭包比的优势 …...

远方游子的归家记:模仿美食网页的制作与实现

前言 2023年的夏天&#xff0c;闲得无聊学了一个礼拜前端知识点。并根据所学知识点模仿制作了一篇网络上公开发布的关于家乡美食的文章。今天才想到有这个不错的案例可以分享出来&#xff0c;以供大家学习参考。 知识点简介 运用的知识点比较简单&#xff0c;常规的div盒子&…...

React JSX?

JSX JSX 是 JavaScript XML 的缩写&#xff0c;它是一种 JavaScript 的语法扩展&#xff0c;允许你在 JavaScript 中写 HTML 类似的代码&#xff0c;常用于 React 中定义 UI 组件的结构。 JSX 示例代码&#xff1a; import React from react;function App() {return (<di…...

【Pandas】pandas DataFrame iterrows

Pandas2.2 DataFrame Indexing, iteration 方法描述DataFrame.head([n])用于返回 DataFrame 的前几行DataFrame.at快速访问和修改 DataFrame 中单个值的方法DataFrame.iat快速访问和修改 DataFrame 中单个值的方法DataFrame.loc用于基于标签&#xff08;行标签和列标签&#…...

docker多架构镜像构建

docker多架构镜像构建 Docker 多架构镜像构建&#xff08;Multi-Architecture Image Build&#xff09;允许你为不同平台&#xff08;如 linux/amd64, linux/arm64, linux/arm/v7 等&#xff09;构建和推送统一的镜像标签&#xff0c;解决在不同硬件架构之间部署的问题。 Doc…...

日志查询 Less命令:/搜索

跟more命令一样&#xff0c;唯一不同的是less命令可以向上翻页&#xff0c;但是more命令不行 注意&#xff1a;按空格或F -- 进行翻页&#xff1b; 按回车 -- 一行一行往下翻&#xff1b; 按q或Q – 表示退出 Page up:一页一页向上翻&#xff1b; 向上键&#xff1a;一行一行…...

[MySQL] 事务管理(一) 事务的基本概念

1.为什么需要事务 1.1 CURD不加控制会有什么问题 我认为要解决上面的问题&#xff0c;CURD必须要有以下的属性 买票的过程必须是原子的 买票相互之间不可以收到影响 买完票需要永久有效 买前&#xff0c;买后的状态必须是确定的 2. 什么是事务&#xff1f; 事务就是一组DML语…...

rk3588 驱动开发(一)字符设备开发

3.字符设备驱动开发 3.1 什么是字符设备驱动 字符设备&#xff1a;就是一个个字节&#xff0c;按照字节流进行读写操作的设备&#xff0c;读写是按照先后顺序的。 举例子&#xff1a;IIC 按键 LED SPI LCD 等 Linux 应用程序调用驱动程序流程&#xff1a; Linux中驱动加载成功…...

【第45节】windows程序的其他反调试手段上篇

目录 引言 一、通过窗口类名和窗口名判断 二、检测调试器进程 三、父进程是否是Explorer 四、RDTSC/GetTickCount时间敏感程序段 五、StartupInfo结构的使用 六、使用BeingDebugged字段 七、 PEB.NtGlobalFlag,Heap.HeapFlags,Heap.ForceFlags 八、DebugPort:CheckRem…...

通过人类和机器人演示进行联合逆向和正向动力学的机器人训练

25年3月来自哥伦比亚大学的论文“Train Robots in a JIF: Joint Inverse and Forward Dynamics with Human and Robot Demonstrations”。 在大型机器人演示数据集上进行预训练是学习各种操作技能的强大技术&#xff0c;但通常受到收集以机器人为中心数据的高成本和复杂性限制…...

云渗透三(对象存储攻防)

对象存储是什么? 对象存储(Cloud Object Storage,COS)是腾讯云提供的⼀种存储海量⽂件的分布式存储服务,⽤户可通过⽹络随时存储和查看数据。腾讯云 COS 使所有⽤户都能使⽤具备⾼扩展性、低成本、可靠和安全的数据存储服务。 COS 通过控制台、API、SDK 和⼯具等多样化⽅…...

ShenNiusModularity项目源码学习(17:ShenNius.Admin.Mvc项目分析-2)

ShenNiusModularity项目的后台管理主页面如下图所示&#xff0c;该页面为ShenNius.Admin.Mvc项目的Views\Home\Index.cshtml&#xff0c;使用的是layuimini后台模板&#xff08;参考文献2&#xff09;&#xff0c;在layuimini的GitHub主页中提供有不同样式的页面模版链接&#…...

Sentinel源码—2.Context和处理链的初始化一

大纲 1.Sentinel底层的核心概念 2.Sentinel中Context的设计思想与源码实现 3.Java SPI机制的引入 4.Java SPI机制在Sentinel处理链中的应用 5.Sentinel默认处理链ProcessorSlot的构建 1.Sentinel底层的核心概念 (1)资源和规则 (2)Context (3)ProcessorSlot (4)Node (1…...

嵌入式C语言高级编程:OOP封装、TDD测试与防御性编程实践

一、面向对象编程(OOP) 尽管 C 语言并非面向对象编程语言&#xff0c;但借助一些编程技巧&#xff0c;也能实现面向对象编程&#xff08;OOP&#xff09;的核心特性&#xff0c;如封装、继承和多态。 1.1 封装 封装是把数据和操作数据的函数捆绑在一起&#xff0c;对外部隐藏…...

C++中unique_lock和lock_guard区别

目录 1.自动锁定与解锁机制 2.灵活性 3.所有权转移 4.可与条件变量配合使用 5.性能开销 在 C 中&#xff0c;std::unique_lock 和 std::lock_guard 都属于标准库 <mutex> 中的互斥锁管理工具&#xff0c;用于简化互斥锁的使用并确保线程安全。但它们存在一些显著区别…...

C# 使用.NET内置的 IObservable<T> 和 IObserver<T>-观察者模式

核心概念 IObservable<T> 表示 可观察的数据源&#xff08;如事件流、实时数据&#xff09;。 关键方法&#xff1a;Subscribe(IObserver<T> observer)&#xff0c;用于注册观察者。 IObserver<T> 表示 数据的接收者&#xff0c;响应数据变化。 三个核心…...

多模态大模型文字识别 vs OCR识别模型

论文简述 多模态大语言模型(Multimodal Large Language Models,简称多模态LLMs)具有高度通用性,能够处理图像描述、文档分析和自动化内容生成等多种任务。这种广泛的适用性使其在不同工业领域都受到了大量关注。 在OCR方面,多模态LLMs的表现超过了专门为OCR设计的模型。这…...

[区块链] 持久化运行区块链 | 并通过HTTP访问

实验目标 以Web服务的形式持久化运行区块链&#xff0c;并通过HTTP接口的形式实现对区块链的操作。 实验内容 构建区块链的区块对象和区块链对象。使用Flask等Web服务框架运行持久化的进程&#xff0c;实现基于HTTP接口实现新区块的添加功能以及传递区块索引查询区块链中的区…...

批量将不同位置的多个文件复制到一个文件夹

在日常工作中&#xff0c;我们常常需要将多个位置的文件集中到一个文件夹中。手动一个个复制文件既费时又容易出错。使用批量文件复制工具&#xff0c;您可以轻松将不同位置的多个文件快速复制到一个文件夹中&#xff0c;大大提高工作效率。 今天给大家介绍一下批量将不同位置的…...

CentOS 下 Zookeeper 常用命令与完整命令列表

一、服务管理命令 常用服务命令 启动 Zookeeper 服务: systemctl start zookeeper # 使用 systemd (推荐) /usr/local/zookeeper/bin/zkServer.sh start # 直接启动 停止服务: systemctl stop zookeeper /usr/local/zookeeper/bin/zkServer.sh stop 查看服务状态…...

React与Vue:哪个框架更适合入门?

React与Vue&#xff1a;选择哪个框架入门&#xff1f; 一、框架定位与发展趋势 1.1 技术背景对比 ‌React‌&#xff1a;Meta&#xff08;原Facebook&#xff09;2013年推出&#xff0c;采用声明式编程范式&#xff0c;专注构建用户界面‌Vue‌&#xff1a;尤雨溪2014年推出…...

突发!GitHub 被曝封禁中国区 IP

昨天&#xff0c;大量国内开发者发现&#xff0c;未登录状态下访问GitHub官网时&#xff0c;页面显示“Access to this site has been restricted.”&#xff0c;中国大陆及香港IP均被限制。尽管GitHub官方尚未发布声明&#xff0c;但多方实测显示&#xff1a; 猪哥亲测在已登…...

向量数据库Qdrant 安装 不使用docker

一、导读 环境&#xff1a;Ubuntu 24.04、Windows 10、WSL 2、Qdrant 1.13.4 背景&#xff1a;换了新工作&#xff0c;使用qdrant作为向量库&#xff0c;需要不使用docker安装 时间&#xff1a;20250415 说明&#xff1a;初入职&#xff0c;不了解&#xff0c;暂且记下 二、…...

基于坐标的神经表示实现零样本学习以用于快速三维多参数定量磁共振成像|文献速递-深度学习医疗AI最新文献

Title 题目 Coordinate-based neural representation enabling zero-shot learning for fast 3D multiparametric quantitative MRI 基于坐标的神经表示实现零样本学习以用于快速三维多参数定量磁共振成像 01 文献速递介绍 定量磁共振成像&#xff08;qMRI&#xff09;能…...

每日算法-250415

每日算法 - 2024-04-15: 今天我们来解决两道 LeetCode 上关于在旋转排序数组中寻找最小值的问题。 153. 寻找旋转排序数组中的最小值 题目描述 思路 核心思想是利用 二分查找。 解题过程 一个升序排序的数组&#xff08;无重复元素&#xff09;在经过若干次旋转后&#xf…...

第16届蓝桥杯省赛JavaB组真题回顾

第16届蓝桥杯省赛已经结束了&#xff0c;第一次参加也是坐牢了4个小时&#xff0c;现在还是来总结一下吧&#xff08;先声明以下的解法&#xff0c;大家可以当作一种思路来看&#xff0c;解法不一定是正解&#xff0c;只是给大家提供一种能够正常想到的思路吧&#xff09; 试题…...

[Godot] C#人物移动抖动解决方案

在写一个2D平台跳跃的游戏代码发现&#xff0c;移动的时候会抖动卡顿的厉害&#xff0c;后来研究了一下抖动问题&#xff0c;有了几种解决方案 1.垂直同步和物理插值问题 这是最常见的可能导致画面撕裂和抖动的原因&#xff0c;大家可以根据自己的需要调整项目设置&#xff0…...

hackmyvm-quick2

收集信息 arp-scan -l nmap 192.168.66.74 dirsearch -u http://192.168.66.74 -e * 访问file.php 尝试查看/etc/passwd 抓包 payload: /file.php?file=/home/andrew/.bashrc /file.php?file=/home/nick/.bashrc...

TDengine 与其他时序数据库对比:InfluxDB/TimescaleDB 选型指南(一)

引言 在当今数字化时代&#xff0c;物联网&#xff08;IoT&#xff09;、工业互联网等领域蓬勃发展&#xff0c;产生了海量的时序数据。这些数据记录了设备状态、传感器读数、业务指标等随时间变化的信息&#xff0c;对于企业的运营监控、数据分析和决策制定起着关键作用。为了…...

Jetson agx Orin 适配Xenomai4

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 目录 文章目录 前言 一、Xenomai 4 二、使用步骤 1.获取源码 2.编译源码 3、安装 4、运行效果 5、libevl 总结 前言 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了…...

Terraform - 理解 Count 和 For_Each 循环

概述 使用 Terraform 时&#xff0c;您可能需要为同一资源创建多个实例。这时 count 和 for_each 循环就派上用场了。这些循环允许您创建具有相同配置但具有不同值的多个资源。本指南将讲解如何在 Terraform 中使用 count 和 for_each 循环。 Terraform 中的 Count Terraform …...

Git本地更新和远端同步操作

1、将远端文件夹拉取到本地 从0—1&#xff1a;克隆 在文件夹空白处点击&#xff1a;Git克隆 输入url&#xff0c;克隆到本地 输入远端文件地址&#xff0c;点击确定&#xff0c;远端文件即可克隆到本地 2、拉取远端更新到本地 文件克隆到本地后&#xff0c;每次更新需…...

激活函数Softmax在不同维度上的应用 | dim参数理解

理解 Softmax 在不同维度上的应用&#xff0c;关键在于明确 Softmax 的作用以及 dim 参数的意义。Softmax 的目标是将输入张量的某些元素转换为概率分布&#xff0c;使得这些元素的和为1。dim 参数决定了沿着哪个维度进行归一化操作。 1. Softmax 的基本原理 Softmax 函数的公…...

地理人工智能中位置编码的综述:方法与应用

以下是对论文 《A Review of Location Encoding for GeoAI: Methods and Applications》 的大纲和摘要整理&#xff1a; A Review of Location Encoding for GeoAI: Methods and Applications 摘要&#xff08;Summary&#xff09; 本文系统综述了地理人工智能&#xff08;G…...

6.DJI-PSDK:psdk订阅无人机高度/速度/GPS/RTK/时间/经纬度等消息及问题解决

DJI-PSDK:psdk订阅无人机高度/速度/GPS/RTK/时间/经纬度等消息 消息订阅可以获取绝大多数无人机的动态信息,包括无人机的姿态、速度、加速度、角速度、高度、GPS 位置、云 台的角度和状态、飞行模式和飞行状态、电机和电池等各类关键信息。 这些信息并不会“一股脑儿地”全部…...

ArrayList的subList的数据仍是集合

ArrayList的subList结果不可强转成ArrayList&#xff0c;否则会抛出 ClassCastException异常 • 级别&#xff1a; 【CRITICAL】 • 规约类型&#xff1a;BUG • 最坏影响&#xff1a; 程序错误&#xff0c;抛出异常 说明&#xff1a;subList 返回的是ArrayList的内部类SubL…...

VS Code怎么换颜色

在 VS Code 中&#xff0c;你可以通过修改主题或自定义编辑器颜色来将界面改为白色。以下是几种方法&#xff1a; 方法 1&#xff1a;切换浅色主题&#xff08;推荐&#xff09; VS Code 自带多个浅色主题&#xff0c;可以直接切换&#xff1a; 按快捷键 Ctrl K&#xff0c;…...