编程记录五
5.第五次小计
5.1 一些转换函数,八百遍了但还是会忘
item.replace('=', ': ')等价于item.split('=').join(':')
// arr.join('\n') 将数组元素用换行符连接成一个字符串:实现换行
"小红:99\n小白:100\n小黄:98"
5.2 关于各种导出,天杀的,老是分不清
在 JavaScript 中,exports
、export
、export default
和 module.exports
是用于模块化代码的不同方式。它们在 CommonJS 和 ES6 模块系统中有不同的用法和引用方式。以下是对它们的详细解释和引用方式:
1. CommonJS 模块系统(Node.js 中使用)-都带s都支持自定义导入名称,就是引入的时候可以起别的名字代表导出的对象
module.exports
- 用途:用于导出整个模块的默认值。
- 示例:
// myModule.js
function greet() {return "Hello, World!";
}function farewell() {return "Goodbye!";
}module.exports = {greet: greet,farewell: farewell
};
- 引用方式:
// app.js
const myModule = require('./myModule');console.log(myModule.greet()); // 输出: Hello, World!
console.log(myModule.farewell()); // 输出: Goodbye!
exports
- 用途:用于导出模块的部分内容。
- 示例:
// myModule.js
function greet() {return "Hello, World!";
}function farewell() {return "Goodbye!";
}exports.greet = greet;
exports.farewell = farewell;
- 引用方式:
const myModule = require('./myModule');
console.log(myModule.greet()); // 输出: Hello, World!
console.log(myModule.farewell()); // 输出: Goodbye!
2. ES6 模块系统(现代浏览器和部分 Node.js 环境支持)-export default支持自定义导入名称,但是export 必须一致
export
- 用途:用于导出模块的部分内容。
- 示例:
// myModule.js
export function greet() {return "Hello, World!";
}export function farewell() {return "Goodbye!";
}
- 引用方式:
import { greet, farewell } from './myModule.js';
console.log(greet()); // 输出: Hello, World!
console.log(farewell()); // 输出: Goodbye!
export default
- 用途:用于导出模块的默认值。
- 示例:
// myModule.js
export default function greet() {return "Hello, World!";
}
- 引用方式:
import greet from './myModule.js';
console.log(greet()); // 输出: Hello, World!
总结
- CommonJS:
module.exports
:导出整个模块的默认值,引用时使用require
。exports
:导出模块的部分内容,引用时使用require
。
- ES6:
export
:导出模块的部分内容,引用时使用import
。export default
:导出模块的默认值,引用时使用import
。
注意事项
- Node.js 默认使用 CommonJS 模块系统,但也可以通过配置支持 ES6 模块。
- 浏览器 默认支持 ES6 模块,但也可以通过工具(如 Webpack)来支持 CommonJS 模块。
5.3 ts 是的,本人学了好多次忘了好多次
interface Props {icon: string;label: string;value: string | number;showCondition: boolean;clickable: boolean;}defineProps<Props>();
//定义两个emit函数,一个click,一个moveto/defineEmits<{click: [];moveTo: [string, number];
}>();
defineEmits<{ click: [] }>()
告诉Vue这个组件可以发出一个click
事件,这个事件没有参数。
5.4 一些已经遗忘的小基础
1.当一个对象没有某个属性的时候,可以直接添加,比如说user是个对象,里面只有user:{age:'18'},现在要添加一个新属性name,直接写成
user.name='张三'
这样就添加进去了
2.关于双叹号(!!)
!! 是JavaScript中的双重否定操作符,用于将任何值转换为布尔值。
// 第一个 ! 将值转换为布尔值并取反
!value // 如果value是真值,返回false;如果是假值,返回true// 第二个 ! 再次取反,得到原始的布尔值
!!value // 如果value是真值,返回true;如果是假值,返回false
3.一个文件里使用了大量图片,现在修改需求,不同品牌使用的图片不一样,第一反应是工作量好大,要一个个改,nonono上班上傻了,真的办法是直接写函数
遇到问题不要直接上手改,要先思考找到最佳的解决办法,使用函数后期就算图片换了也不怕,只需要在函数里面换掉就可以,如果直接上手改,工作量就会很大,而且后期维护也很不方便
// 根据品牌选择图片的函数const getImageByBrand = (type: 'green' | 'grey' | 'red') => {if (route.query.brand === 'Supcon') {switch (type) {case 'green':return SupconGreen;case 'grey':return SupconGrey;case 'red':return SupconRed;default:return SupconGreen;}} else {// 默认使用 Hollysys 的图片switch (type) {case 'green':return controlsGreen;case 'grey':return controlsGrey;case 'red':return controlsRed;default:return controlsGreen;}}};....................const nodePic = c.isPlaceholder? jiahao: c.wrong === 1? getImageByBrand('red'): getImageByBrand('green');
5.5 antv x6的布局设计
问题1:A品牌需要16个卡件,分成2列八行,第一列是前八个,B品牌需要64个,8列8行,前八个在第一个,我第一次听到简直烧脑,
解决:但其实很简单,主要是数学思维,用到的是/还有%,一个取余数一个取除数,这样就可以把对应的放在同一行或者同一列
问题2:如果卡件类型是io的话,是要占据普通卡件四个位置的,之前的逻辑是,如果后端没有返回卡件数据,那么就加一个占位图片,现在如果是io的话,就是占据四个位置,也就是一个io卡件图片然后占据四个槽位取代之前的一个卡件图片加三个占位图片
解决:Math.floor()
是 JavaScript 中的一个数学函数,作用是对一个数进行向下取整,即返回小于或等于该数的最大整数。简单来说,它会 “砍掉” 数字的小数部分,只保留整数部分,并且始终向更小的方向取整(即使小数部分大于 0.5)。
为什么要减1-减 1 是为了让 “1-8”“9-16” 这样的连续槽位区间,通过除法取整后得到相同的行号,否则槽位 8 会被错误分到下一行,破坏 “每行 8 个槽” 的规则。
情况1:后端数据返回含有io卡件
const slotMap = {};
allCards.forEach((card) => {const slot = parseInt(card.slot!);slotMap[slot] = card;// 如果是IO卡件,标记相邻的3个槽位也被占用if (card.cardt === 'IO') {const nextSlot1 = slot + 1;const nextSlot2 = slot + 2;const nextSlot3 = slot + 3;// 检查是否在同一行(8x8网格中,每行8个槽位)const currentRow = Math.floor((slot - 1) / 8);const nextRow1 = Math.floor((nextSlot1 - 1) / 8);const nextRow2 = Math.floor((nextSlot2 - 1) / 8);const nextRow3 = Math.floor((nextSlot3 - 1) / 8);// 只有同一行的槽位才被占用if (nextRow1 === currentRow) {slotMap[nextSlot1] = { ...card, isOccupiedByIO: true };}if (nextRow2 === currentRow) {slotMap[nextSlot2] = { ...card, isOccupiedByIO: true };}if (nextRow3 === currentRow) {slotMap[nextSlot3] = { ...card, isOccupiedByIO: true };}}});.....
....const isOccupiedByIO = card?.isOccupiedByIO;// 如果被IO卡件占用,跳过这个槽位,这样就不会在遍历数据的时候给该槽位数据添加占位图片if (isOccupiedByIO) {continue;}
情况2:如果io卡件已经展示 现在需要删除 对应的占位符一次就要展示四个,所以还是延续上面的逻辑
// 如果是IO卡件,需要重新生成被占用的4个槽位的占位符// 如果是Emerson品牌的AI/AO/DI/DO卡件,也需要重新生成占位符if (cellData.cardt === 'IO' ||(route.query.brand === 'Emerson' && ['AI', 'AO', 'DI', 'DO'].includes(cellData.cardt))) {console.log(`删除${cellData.cardt}卡件,重新生成占位符`);const slot = parseInt(cellData.slot);const isEmerson = route.query.brand === 'Emerson';const cardsPerRow = isEmerson ? 8 : 20;// 计算卡件占用的槽位let occupiedSlots;if (cellData.cardt === 'IO') {// IO卡件占用4个槽位occupiedSlots = [slot, slot + 1, slot + 2, slot + 3];} else {// AI/AO/DI/DO卡件只占用1个槽位occupiedSlots = [slot];}// 为每个被占用的槽位生成占位符occupiedSlots.forEach((occupiedSlot, index) => {const rowIdx = Math.floor((occupiedSlot - 1) / cardsPerRow);const colIdx = ((occupiedSlot - 1) % cardsPerRow) + 1;// 计算位置坐标let y, x;if (isEmerson) {y = CTRL_H + 20 - 30 + rowIdx * CARD_H_EMERSON;x = 45 + (colIdx - 1) * (CARD_W * 2.3);} else {y = CTRL_H + 50 + rowIdx * (CARD_H + CARD_GAP);x = 55 + (colIdx - 1) * (CARD_W - 1.9);}// 生成占位符图片const placeholderImage = getCardImage('', '0', false);// 创建占位符节点const placeholderNode = graph.addNode({shape: 'image',id: `placeholder-${rowIdx}-${colIdx}-${Date.now()}`,x: x,y: y,width: isEmerson ? EMERSON_WIDTH : CARD_W,height: isEmerson ? EMERSON_HEIGHT : CARD_H,attrs: {image: {'xlink:href': placeholderImage,},},data: {typee: 'io',slot: occupiedSlot.toString(),iocardDataId: `placeholder-${rowIdx}-${colIdx}-${Date.now()}`,isPlaceholder: true,// 其他默认属性(运行信息默认值,这是新增的卡件)channelUtilizationCount: '16',maxSignalOutputRange: '12mA',...........maxOutputLoadCapacity: '750Ω',sysSideFpgaPowDiagnosisItem: '2.5032',},});});}
情况3:用户操作,鼠标右键占位图片节点来新增io卡件,此时一次性就要去掉四个占位图片
// 如果是IO卡件,需要更新为四个槽位的尺寸和位置,并删除被占用的占位符if (cardType === 'IO') {const isEmerson = route.query.brand === 'Emerson';const cardWidth = isEmerson ? EMERSON_WIDTH * 4 : CARD_W * 4;const cardHeight = isEmerson ? EMERSON_HEIGHT : CARD_H; // 不要加倍高度// 获取当前节点位置,IO卡件从起始位置开始,不需要偏移const currentPos = targetNode.position();// 更新节点尺寸,位置保持不变targetNode.resize(cardWidth, cardHeight);// 删除被IO卡件占用的4个槽位的其他占位符const slot = parseInt(newData.slot);const cardsPerRow = isEmerson ? 8 : 20;console.log(slot, 'slot');const occupiedSlots = [slot + 1, slot + 2, slot + 3]; // 只处理其他3个相邻槽位,不处理被点击的槽位// 查找并删除被占用的占位符节点occupiedSlots.forEach((occupiedSlot, index) => {const rowIdx = Math.floor((occupiedSlot - 1) / cardsPerRow);const colIdx = ((occupiedSlot - 1) % cardsPerRow) + 1;// 查找对应的占位符节点const placeholderNodes = graph.getNodes().filter((node) => {const nodeData = node.getData();return (nodeData.typee === 'io' &&(nodeData.slot === occupiedSlot ||nodeData.slot === occupiedSlot.toString()) &&node.id.startsWith('placeholder-'));});console.log(`槽位 ${occupiedSlot} 找到的占位符节点:`, placeholderNodes.length);// 删除找到的占位符节点placeholderNodes.forEach((node) => {console.log(`删除占位符节点: ${node.id}`);graph.removeCell(node);});});}
5.6 antv x6的attr属性
attr里可设置很多东西 ,目前使用过的
attrs: {image: {'xlink:href': cardImage, //显示图片},// 添加卡件号码文本(Emerson品牌不显示数字)text: isEmerson //定义文本,可以显示在图片上? {}: {text: colIdx.toString(),fontSize: 5,fill: 'white',textAnchor: 'middle',textVerticalAnchor: 'middle',fontWeight: 'bold',x: cardWidth - 12,y: cardHeight / 5,},},
修改属性的话可以使用 ,注意一下使用/指定是哪个属性
node.attr('text/fontSize', currentFontSize * scaleRatio);
cell.attr('image/xlink:href', placeholderImage);
targetNode.setAttrs({image: {href: cardImage,},});