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

制作我的计算器

1. 界面布局

新建项目 MyCalculator,开始布局。

2. 静态布局

代码如下:

// etc/pages/Index.ets
@Entry
@Component
struct Index {build() {Column() {/*** 运算区*/Column() {TextInput({ text: '12x13' }).height('100%').fontSize(32).enabled(false).fontColor(Color.Black).textAlign(TextAlign.End).backgroundColor('#f5f5f5')}.width('100%').height(86).alignItems(HorizontalAlign.End).margin({right: 20,top: 120})/*** 结果区*/Column() {Text('123').fontSize('32fp').fontColor('#182431')}.width('100%').height(42).alignItems(HorizontalAlign.End).margin({right: 20,bottom: 64})/*** 输入面板区*/Column() {Row() {// 多行子元素Column() {// 多列子元素Column() {Column() {Image($r('app.media.ic_clean')).width(32).height(32)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('7').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('4').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('1').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('%').fontSize('32fp').width(25).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)}.layoutWeight(1).margin({top: 30,bottom: 30})Column() {// 多列子元素Column() {Column() {Image($r('app.media.ic_div')).width(32).height(32)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('8').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('5').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('2').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('0').fontSize('32fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)}.layoutWeight(1).margin({top: 30,bottom: 30})Column() {// 多列子元素Column() {Column() {Image($r('app.media.ic_mul')).width(32).height(32)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('9').fontSize(32).width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('6').fontSize(32).width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('3').fontSize(32).width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Text('.').fontSize('42fp').width(19).height(43)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)}.layoutWeight(1).margin({top: 30,bottom: 30})Column() {// 多列子元素Column() {Column() {Image($r('app.media.ic_del')).width(30).height(20)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Image($r('app.media.ic_min')).width(24).height(24)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Image($r('app.media.ic_add')).width(32).height(32)}.width(70).height(60).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor(Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center)Column() {Column() {Image($r('app.media.ic_equ')).width(32).height(32)}.width(70).height(125).borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor('#007DFF').alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight(2).width('100%').justifyContent(FlexAlign.Center)}.layoutWeight(1).margin({top: 30,bottom: 30})}.height('100%').alignItems(VerticalAlign.Top).margin({left: 20,right: 20})}.layoutWeight(1).width('100%').backgroundColor('#f8f8ff')}.height('100%').backgroundColor('#f5f5f5')}
}

3. 动态布局

3.1. 定义视图模型

  • PressKeysItem.ets
// ets/viewmodel/PressKeysModel.etsexport class PressKeysItem {flag: numberwidth: stringheight: stringvalue: stringsource?: Resourceconstructor(flag: number, width: string, height: string, value: string, source?: Resource) {this.flag = flagthis.width = widththis.height = heightthis.value = valuethis.source = source}
}export class PressKeysInnerObject {id: stringdata: PressKeysItemconstructor(id: string, data: PressKeysItem) {this.id = idthis.data = data}
}export class PressKeysOuterObject {id: stringdata: Array<PressKeysInnerObject>constructor(id: string, data: Array<PressKeysInnerObject>) {this.id = idthis.data = data}
}
  • PressKeysViewModel.ets
// ets/viewmodel/PressKeysViewModel.etsimport { PressKeysInnerObject, PressKeysOuterObject, PressKeysItem } from './PressKeysModel'export class PressKeysViewModel {getPressKeys(): Array<PressKeysOuterObject> {return [new PressKeysOuterObject('01',[new PressKeysInnerObject('010', new PressKeysItem(0, '32vp', '32vp', 'clean', $r('app.media.ic_clean'))),new PressKeysInnerObject('011', new PressKeysItem(1, '19vp', '43vp', '7')),new PressKeysInnerObject('012', new PressKeysItem(1, '19vp', '43vp', '4')),new PressKeysInnerObject('013', new PressKeysItem(1, '19vp', '43vp', '1')),new PressKeysInnerObject('014', new PressKeysItem(1, '25vp', '43vp', '%'))]),new PressKeysOuterObject('02',[new PressKeysInnerObject('020', new PressKeysItem(0, '32vp', '32vp', 'div', $r('app.media.ic_div'))),new PressKeysInnerObject('021', new PressKeysItem(1, '19vp', '43vp', '8')),new PressKeysInnerObject('022', new PressKeysItem(1, '19vp', '43vp', '5')),new PressKeysInnerObject('023', new PressKeysItem(1, '19vp', '43vp', '2')),new PressKeysInnerObject('024', new PressKeysItem(1, '19vp', '43vp', '0'))]),new PressKeysOuterObject('03',[new PressKeysInnerObject('030', new PressKeysItem(0, '32vp', '32vp', 'mul', $r('app.media.ic_mul'))),new PressKeysInnerObject('031', new PressKeysItem(1, '19vp', '43vp', '9')),new PressKeysInnerObject('032', new PressKeysItem(1, '19vp', '43vp', '6')),new PressKeysInnerObject('033', new PressKeysItem(1, '19vp', '43vp', '3')),new PressKeysInnerObject('034', new PressKeysItem(1, '19vp', '43vp', '.'))]),new PressKeysOuterObject('04',[new PressKeysInnerObject('040', new PressKeysItem(0, '30.48vp', '20vp', 'del', $r('app.media.ic_del'))),new PressKeysInnerObject('041', new PressKeysItem(0, '24vp', '24vp', 'min', $r('app.media.ic_min'))),new PressKeysInnerObject('042', new PressKeysItem(0, '32vp', '32vp', 'add', $r('app.media.ic_add'))),new PressKeysInnerObject('043', new PressKeysItem(0, '32vp', '32vp', 'equ', $r('app.media.ic_equ')))])]}
}let keysModel = new PressKeysViewModel()export default keysModel as PressKeysViewModel

3.2. 布局代码

// etc/pages/Index.etsimport { PressKeysOuterObject, PressKeysInnerObject } from '../viewmodel/PressKeysModel'
import keysModel from '../viewmodel/PressKeysViewModel'// 入口组件定义
@Entry
@Component
struct Index {// 构建组件界面build() {Column() {/*** 运算区*/Column() {TextInput({ text: '12x13' }) // 显示输入的格式化值.height('100%').fontSize(32).enabled(false) // 禁用输入.fontColor(Color.Black).textAlign(TextAlign.End).backgroundColor('#f5f5f5')}.width('100%').height(86).alignItems(HorizontalAlign.End).margin({right: 20,top: 120})/*** 结果区*/Column() {Text('123') // 显示计算结果.fontSize('32fp').fontColor('#182431')}.width('100%').height(42).alignItems(HorizontalAlign.End).margin({right: 20,bottom: 64})/*** 输入面板区*/Column() {Row() {// 遍历键盘模型,生成按键ForEach(keysModel.getPressKeys(), (columnItem: PressKeysOuterObject, columnItemIndex: number) => {Column() {ForEach(columnItem.data, (keyItem: PressKeysInnerObject, keyItemIndex: number) => {Column() {Column() {// 根据按键类型生成图像或文本if (keyItem.data.flag === 0) {Image(keyItem.data.source !== undefined ? keyItem.data.source : '').width(keyItem.data.width).height(keyItem.data.height)} else {Text(keyItem.data.value).fontSize(keyItem.data.value === '.' ? '42fp' : '32fp')}}.width(70).height((columnItemIndex === keysModel.getPressKeys().length - 1 &&keyItemIndex === columnItem.data.length - 1) ?'125vp' : '60vp').borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor((columnItemIndex === keysModel.getPressKeys().length - 1 &&keyItemIndex === columnItem.data.length - 1) ?'#007DFF' : Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.layoutWeight((columnItemIndex === keysModel.getPressKeys().length - 1 &&keyItemIndex === columnItem.data.length - 1) ?2 : 1).width('100%').justifyContent(FlexAlign.Center)}, (keyItem: PressKeysInnerObject) => keyItem.id)}.layoutWeight(1).margin({top: 30,bottom: 30})}, (columnItem: PressKeysOuterObject) => columnItem.id)}.height('100%').alignItems(VerticalAlign.Top).margin({left: 20,right: 20})}.layoutWeight(1).width('100%').backgroundColor('#f8f8ff')}.height('100%').backgroundColor('#f5f5f5')}
}

4. 计算逻辑

4.1. 常量定义文件

// ets/constants/CommonConstants.ets
export class CommonConstants {// 定义操作符字符串static readonly OPERATORS: string = '+-×÷';// 定义操作符优先级字符串static readonly OPERATORS_PRIORITY: string = '×÷';// 定义各个操作符static readonly ADD: string = '+';static readonly MIN: string = '-';static readonly MUL: string = '×';static readonly DIV: string = '÷';// 定义其他常量static readonly PERCENT_SIGN: string = '%'; // 百分号static readonly DOTS: string = '.'; // 小数点static readonly TWO: number = 2; // 数字2static readonly TEN: number = 10; // 数字10static readonly ONE_HUNDRED: string = '100'; // 字符串'100'static readonly INPUT_LENGTH_MAX: number = 9; // 最大输入长度static readonly INDEX_TWO: number = 2; // 索引2static readonly NUM_MAX_LEN: number = 16; // 数字最大长度static readonly E: string = 'e'; // 科学计数法中的estatic readonly ZERO: string = '0'; // 字符串'0'static readonly ZERO_DOTS: string = '0.'; // 字符串'0.'
}// 定义操作符的枚举
export enum Symbol {ADD = 'add',MIN = 'min',MUL = 'mul',DIV = 'div',CLEAN = 'clean',DEL = 'del',EQU = 'equ'
}// 定义操作符优先级的枚举
export enum Priority {HIGH = 2,MEDIUM = 1,LOW = 0
}// 定义符号枚举,用于识别不同的数学符号
export enum SymbolicEnumeration {ADD = '+',MIN = '-',MUL = '×',DIV = '÷'
}

4.2. 判空工具类

// ets/uitls/CheckEmptyUtil.etsclass CheckEmptyUtil {// 检查对象或字符串是否为空isEmpty(obj: object | string): boolean {return (typeof obj === 'undefined' || obj === null || obj === ''); // 判断是否为 undefined、null 或空字符串}// 检查字符串是否为空(去除空格后)checkStrIsEmpty(str: string): boolean {return str.trim().length === 0; // 如果去除空格后长度为 0,返回 true}// 检查数组是否为空isEmptyArr(arr: Array<string>) {return arr.length === 0; // 如果数组长度为 0,返回 true}
}export default new CheckEmptyUtil(); // 导出 CheckEmptyUtil 的单例

4.3. 计算器核心类

// ets/uitis/CalculateUtil.etsimport CheckEmptyUtil from './CheckEmptyUtil' // 导入检查空值的工具类
import { CommonConstants, Priority, SymbolicEnumeration } from '../constants/CommonConstants' // 导入常量和枚举class CalculateUtil {// 检查给定值是否为操作符isSymbol(value: string) {if (CheckEmptyUtil.isEmpty(value)) {return // 如果值为空,返回 undefined}// 判断值是否在操作符列表中return (CommonConstants.OPERATORS.indexOf(value) !== -1);}// 获取操作符的优先级getPriority(value: string): number {if (CheckEmptyUtil.isEmpty(value)) {return Priority.LOW; // 如果值为空,默认优先级为低}let result = 0; // 初始化优先级结果switch (value) {case SymbolicEnumeration.ADD: // 如果是加法case SymbolicEnumeration.MIN: // 如果是减法result = Priority.MEDIUM; // 设置为中等优先级break;case SymbolicEnumeration.MUL: // 如果是乘法case SymbolicEnumeration.DIV: // 如果是除法result = Priority.HIGH; // 设置为高优先级break;default:result = Priority.LOW; // 其他符号默认为低优先级break;}return result; // 返回优先级结果}// 比较两个操作符的优先级comparePriority(arg1: string, arg2: string): boolean {// 检查任一操作符是否为空if (CheckEmptyUtil.isEmpty(arg1) || CheckEmptyUtil.isEmpty(arg2)) {return false // 如果有空值,返回 false}// 返回 arg1 是否优先级低于等于 arg2return (this.getPriority(arg1) <= this.getPriority(arg2));}// 解析表达式,使用逆波兰表示法(后缀表达式)// 3×5+4÷2parseExpression(expressions: Array<string>): string {// 检查表达式数组是否为空if (CheckEmptyUtil.isEmpty(expressions)) {return 'NaN' // 如果为空,返回 'NaN'}let len = expressions.length; // 获取表达式的长度let outputStack: string[] = []; // 初始化操作符栈let outputQueue: string[] = []; // 初始化输出队列// 处理表达式数组expressions.forEach((item: string, index: number) => {// 处理百分号,将百分数转换为小数if (item.indexOf(CommonConstants.PERCENT_SIGN) !== -1) {expressions[index] = (this.mulOrDiv(item.slice(0, item.length - 1),CommonConstants.ONE_HUNDRED, CommonConstants.DIV)).toString();}// 如果是最后一个元素且为符号,则移除if ((index === len - 1) && this.isSymbol(item)) {expressions.pop(); // 从数组中移除最后的符号}});// 遍历表达式,构建输出队列while (expressions.length > 0) {let current: string | undefined = expressions.shift(); // 获取当前元素if (current !== undefined) { // 如果当前元素存在if (this.isSymbol(current)) { // 如果当前元素是符号// 比较当前符号与栈顶符号的优先级while (outputStack.length > 0 && this.comparePriority(current, outputStack[outputStack.length - 1])) {let popValue: string | undefined = outputStack.pop(); // 弹出栈顶符号if (popValue !== undefined) {outputQueue.push(popValue); // 将弹出的符号加入输出队列}}outputStack.push(current); // 将当前符号推入栈中} else {outputQueue.push(current); // 如果不是符号,直接加入输出队列}}}// 将栈中剩余的符号加入输出队列while (outputStack.length > 0) {let popValue: string | undefined = outputStack.pop();if (popValue !== undefined) {outputQueue.push(popValue); // 将剩余的符号加入输出队列}}// 处理输出队列,计算最终结果return this.dealQueue(outputQueue);}// 处理输出队列,计算结果dealQueue(queue: Array<string>): string {// 检查队列是否为空if (CheckEmptyUtil.isEmpty(queue)) {return 'NaN'; // 如果为空,返回 'NaN'}let outputStack: string[] = []; // 初始化输出栈// 遍历队列,计算结果while (queue.length > 0) {let current: string | undefined = queue.shift(); // 获取当前元素if (current !== undefined) { // 如果当前元素存在if (!this.isSymbol(current)) { // 如果不是符号,加入输出栈outputStack.push(current);} else {// 弹出栈顶两个元素作为操作数let second: string | undefined = outputStack.pop(); // 弹出第二个操作数let first: string | undefined = outputStack.pop(); // 弹出第一个操作数if (first !== undefined && second !== undefined) {// 计算结果并加入输出栈let calResultValue: string = this.calResult(first, second, current);outputStack.push(calResultValue); // 将计算结果加入栈}}}}// 检查输出栈的状态,确保只有一个结果if (outputStack.length !== 1) {return 'NaN'; // 如果不止一个元素,返回 'NaN'} else {// 处理结果的小数点let end: string = outputStack[0]?.endsWith(CommonConstants.DOTS) ?outputStack[0].substring(0, outputStack[0].length - 1) : outputStack[0];return end; // 返回最终结果}}// 计算两个操作数和符号之间的结果calResult(arg1: string, arg2: string, symbol: string): string {// 检查参数是否为空if (CheckEmptyUtil.isEmpty(arg1) || CheckEmptyUtil.isEmpty(arg2) || CheckEmptyUtil.isEmpty(symbol)) {return 'NaN'; // 如果有空值,返回 'NaN'}let result = 0; // 初始化计算结果switch (symbol) {case SymbolicEnumeration.ADD:// 处理加法result = this.add(arg1, arg2, CommonConstants.ADD);break;case SymbolicEnumeration.MIN:// 处理减法result = this.add(arg1, arg2, CommonConstants.MIN);break;case SymbolicEnumeration.MUL:// 处理乘法result = this.mulOrDiv(arg1, arg2, CommonConstants.MUL);break;case SymbolicEnumeration.DIV:// 处理除法result = this.mulOrDiv(arg1, arg2, CommonConstants.DIV);break;default:break; // 其他操作符不处理}return this.numberToScientificNotation(result); // 返回结果(可能为科学计数法)}// 处理加法add(arg1: string, arg2: string, symbol: string): number {let addFlag = (symbol === CommonConstants.ADD); // 判断是否为加法// 处理科学计数法if (this.containScientificNotation(arg1) || this.containScientificNotation(arg2)) {if (addFlag) {return Number(arg1) + Number(arg2); // 直接计算加法}return Number(arg1) - Number(arg2); // 直接计算减法}// 处理零的情况arg1 = (arg1 === CommonConstants.ZERO_DOTS) ? '0' : arg1;arg2 = (arg2 === CommonConstants.ZERO_DOTS) ? '0' : arg2;// 分割小数部分let leftArr = arg1.split(CommonConstants.DOTS);let rightArr = arg2.split(CommonConstants.DOTS);// 获取小数部分长度let leftLen = leftArr.length > 1 ? leftArr[1] : '';let rightLen = rightArr.length > 1 ? rightArr[1] : '';// 获取最大小数部分长度let maxLen = Math.max(leftLen.length, rightLen.length);let multiples = Math.pow(CommonConstants.TEN, maxLen); // 计算倍数// 处理加法或减法if (addFlag) {return Number(((Number(arg1) * multiples + Number(arg2) * multiples) / multiples).toFixed(maxLen));}return Number(((Number(arg1) * multiples - Number(arg2) * multiples) / multiples).toFixed(maxLen));}// 处理乘法和除法mulOrDiv(arg1: string, arg2: string, symbol: string): number {let mulFlag = (symbol === CommonConstants.MUL); // 判断是否为乘法// 处理科学计数法if (this.containScientificNotation(arg1) || this.containScientificNotation(arg2)) {if (mulFlag) {return Number(arg1) * Number(arg2); // 直接计算乘法}return Number(arg1) / Number(arg2); // 直接计算除法}// 获取小数部分长度let leftLen = arg1.split(CommonConstants.DOTS)[1] ? arg1.split(CommonConstants.DOTS)[1].length : 0;let rightLen = arg2.split(CommonConstants.DOTS)[1] ? arg2.split(CommonConstants.DOTS)[1].length : 0;// 处理乘法if (mulFlag) {return Number(arg1.replace(CommonConstants.DOTS, '')) *Number(arg2.replace(CommonConstants.DOTS, '')) / Math.pow(CommonConstants.TEN, leftLen + rightLen);}// 处理除法return Number(arg1.replace(CommonConstants.DOTS, '')) /(Number(arg2.replace(CommonConstants.DOTS, '')) / Math.pow(CommonConstants.TEN, rightLen - leftLen));}// 检查字符串是否包含科学计数法containScientificNotation(arg: string) {return (arg.indexOf(CommonConstants.E) !== -1); // 判断是否含有 'e'}// 将结果转换为科学计数法格式numberToScientificNotation(result: number) {// 处理无穷大情况if (result === Number.NEGATIVE_INFINITY || result === Number.POSITIVE_INFINITY) {return 'NaN'; // 返回 'NaN',表示计算出错}let resultStr = JSON.stringify(result); // 将结果转为字符串// 如果已经是科学计数法,直接返回if (this.containScientificNotation(resultStr)) {return resultStr;}let prefixNumber = (resultStr.indexOf(CommonConstants.MIN) === -1) ? 1 : -1; // 处理负数前缀result *= prefixNumber; // 应用前缀符号// 检查结果长度是否超过限制if (resultStr.replace(CommonConstants.DOTS, '').replace(CommonConstants.MIN, '').length <CommonConstants.NUM_MAX_LEN) {return resultStr; // 如果长度合适,直接返回}// 计算科学计数法的指数部分let suffix = (Math.floor(Math.log(result) / Math.LN10));let prefix = (result * Math.pow(CommonConstants.TEN, -suffix) * prefixNumber); // 计算系数return (prefix + CommonConstants.E + suffix); // 返回科学计数法表示}
}export default new CalculateUtil(); // 导出 CalculateUtil 的单例

4.4. 首页代码调用和实现

// etc/pages/Index.etsimport { PressKeysOuterObject, PressKeysInnerObject } from '../viewmodel/PressKeysModel'
import keysModel from '../viewmodel/PressKeysViewModel'
import CalculateUtil from '../utils/CalculateUtil'
import CheckEmptyUtil from '../utils/CheckEmptyUtil'
import { CommonConstants, Symbol } from '../constants/CommonConstants'
import Logger from '../utils/Logger'// 入口组件定义
@Entry
@Component
struct Index {// 输入的表达式@State inputValue: string = ''// 计算结果@State calValue: string = ''// 存储表达式的数组private expressions: Array<string> = []// 构建组件界面build() {Column() {/*** 运算区*/Column() {TextInput({ text: this.resultFormat(this.inputValue) }) // 显示输入的格式化值.height('100%').fontSize(32).enabled(false) // 禁用输入.fontColor(Color.Black).textAlign(TextAlign.End).backgroundColor('#f5f5f5')}.width('100%').height(86).alignItems(HorizontalAlign.End).margin({right: 20,top: 120})/*** 结果区*/Column() {Text(this.resultFormat(this.calValue)) // 显示计算结果.fontSize('32fp').fontColor('#182431')}.width('100%').height(42).alignItems(HorizontalAlign.End).margin({right: 20,bottom: 64})/*** 输入面板区*/Column() {Row() {// 遍历键盘模型,生成按键ForEach(keysModel.getPressKeys(), (columnItem: PressKeysOuterObject, columnItemIndex: number) => {Column() {ForEach(columnItem.data, (keyItem: PressKeysInnerObject, keyItemIndex: number) => {Column() {Column() {// 根据按键类型生成图像或文本if (keyItem.data.flag === 0) {Image(keyItem.data.source !== undefined ? keyItem.data.source : '').width(keyItem.data.width).height(keyItem.data.height)} else {Text(keyItem.data.value).fontSize(keyItem.data.value === '.' ? '42fp' : '32fp')}}.width(70).height((columnItemIndex === keysModel.getPressKeys().length - 1 &&keyItemIndex === columnItem.data.length - 1) ?'125vp' : '60vp').borderWidth(1).borderColor("#f5f5f5").borderRadius(36).backgroundColor((columnItemIndex === keysModel.getPressKeys().length - 1 &&keyItemIndex === columnItem.data.length - 1) ?'#007DFF' : Color.White).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center).onClick(() => {this.inputValue = keyItem.data.value // 更新输入值// 根据按键类型调用相应的输入方法if (keyItem.data.flag === 0) {this.inputSymbol(keyItem.data.value)} else {this.inputNumber(keyItem.data.value)}})}.layoutWeight((columnItemIndex === keysModel.getPressKeys().length - 1 &&keyItemIndex === columnItem.data.length - 1) ?2 : 1).width('100%').justifyContent(FlexAlign.Center)}, (keyItem: PressKeysInnerObject) => keyItem.id)}.layoutWeight(1).margin({top: 30,bottom: 30})}, (columnItem: PressKeysOuterObject) => columnItem.id)}.height('100%').alignItems(VerticalAlign.Top).margin({left: 20,right: 20})}.layoutWeight(1).width('100%').backgroundColor('#f8f8ff')}.height('100%').backgroundColor('#f5f5f5')}// 输入符号处理inputSymbol(value: string) {if (CheckEmptyUtil.isEmpty(value)) {return // 检查输入是否为空}let len: number = this.expressions.length // 获取当前表达式长度switch (value) {case Symbol.CLEAN:this.expressions = [] // 清空表达式this.calValue = ''break;case Symbol.DEL:this.inputDelete(len) // 删除操作break;case Symbol.EQU:if (len === 0) {return; // 如果没有表达式,直接返回}// 计算结果并更新状态(this.getResult() as Promise<boolean>).then((result: boolean) => {if (!result) {return}this.inputValue = this.calValue // 更新输入值为计算结果this.calValue = ''this.expressions = []this.expressions.push(this.inputValue)})breakdefault:this.inputOperators(len, value) // 处理其他符号输入break;}this.formatInputValue() // 格式化输入值}// 输入数字处理inputNumber(value: string) {if (CheckEmptyUtil.isEmpty(value)) {return // 检查输入是否为空}let len: number = this.expressions.length // 获取当前表达式长度let last: string = len > 0 ? this.expressions[len - 1] : '' // 获取最后一个输入let secondLast: string | undefined = len > 1 ? this.expressions[len - CommonConstants.TWO] : undefined // 获取倒数第二个输入// 验证输入是否合法if (!this.validateEnter(last, value)) {return}// 根据当前表达式状态更新表达式if (!last) {this.expressions.push(value) // 如果没有输入,直接添加} else if (!secondLast) {this.expressions[len - 1] += value // 如果只有一个输入,拼接}if (secondLast && CalculateUtil.isSymbol(secondLast)) {this.expressions[len - 1] += value // 如果倒数第二个是符号,拼接}if (secondLast && !CalculateUtil.isSymbol(secondLast)) {this.expressions.push(value) // 如果倒数第二个不是符号,添加新的输入}this.formatInputValue(); // 格式化输入值if (value !== CommonConstants.DOTS) {this.getResult() // 如果输入不是小数点,计算结果}}// 验证输入合法性validateEnter(last: string, value: string) {if (!last && value === CommonConstants.PERCENT_SIGN) {return false // 不能在没有输入的情况下输入百分号}if ((last === CommonConstants.MIN) && (value === CommonConstants.PERCENT_SIGN)) {return false // 负号后不能直接输入百分号}if (last.endsWith(CommonConstants.PERCENT_SIGN)) {return false // 不能在百分号后输入其他数字}if ((last.indexOf(CommonConstants.DOTS) !== -1) && (value === CommonConstants.DOTS)) {return false // 不能输入多个小数点}if ((last === '0') && (value !== CommonConstants.DOTS) &&(value !== CommonConstants.PERCENT_SIGN)) {return false // 0后不能输入非小数和百分号的数字}return true // 验证通过}// 删除操作inputDelete(len: number) {if (len === 0) {return // 如果没有输入,直接返回}let last = this.expressions[len - 1] // 获取最后一个输入let lastLen: number = last.length // 获取最后一个输入的长度if (lastLen === 1) {this.expressions.pop() // 如果长度为1,删除该输入len = this.expressions.length} else {this.expressions[len - 1] = last.slice(0, last.length - 1) // 删除最后一个字符}if (len === 0) {this.inputValue = ''this.calValue = ''return // 如果没有输入,清空值}if (!CalculateUtil.isSymbol(this.expressions[len - 1])) {this.getResult() // 如果最后一个不是符号,重新计算结果}}// 输入运算符处理inputOperators(len: number, value: string) {let last: string | undefined = len > 0 ? this.expressions[len - 1] : undefined // 获取最后一个输入let secondLast: string | undefined = len > 1 ? this.expressions[len - CommonConstants.TWO] : undefined // 获取倒数第二个输入if (!last && (value === Symbol.MIN)) {this.expressions.push(this.getSymbol(value)); // 处理负号的情况return}if (!last) {return // 如果没有输入,返回}if (!CalculateUtil.isSymbol(last)) {this.expressions.push(this.getSymbol(value)) // 如果最后一个不是符号,添加新的符号return}if ((value === Symbol.MIN) &&(last === CommonConstants.MIN || last === CommonConstants.ADD)) {this.expressions.pop() // 处理负号替换this.expressions.push(this.getSymbol(value))return}if (!secondLast) {return // 如果倒数第二个输入不存在,返回}if (value !== Symbol.MIN) {this.expressions.pop() // 如果不是负号,删除最后一个符号}if (CalculateUtil.isSymbol(secondLast)) {this.expressions.pop() // 如果倒数第二个也是符号,删除}this.expressions.push(this.getSymbol(value)) // 添加新的符号}// 获取符号对应的字符串getSymbol(value: string) {if (CheckEmptyUtil.isEmpty(value)) {return ''}let symbol = ''switch (value) {case Symbol.ADD:symbol = CommonConstants.ADDbreakcase Symbol.MIN:symbol = CommonConstants.MINbreakcase Symbol.MUL:symbol = CommonConstants.MULbreak;case Symbol.DIV:symbol = CommonConstants.DIVbreakdefault:break}return symbol // 返回对应的符号}// 深拷贝表达式数组deepCopy(): Array<string> {let copyExpressions: Array<string> = Array.from(this.expressions) // 使用 Array.from 深拷贝数组return copyExpressions}// 计算结果async getResult(): Promise<boolean> {let calResult = CalculateUtil.parseExpression(this.deepCopy()) // 解析表达式if (calResult === 'NaN') {this.calValue = this.resourceToString($r('app.string.error')) // 处理计算错误return false;}this.calValue = calResult; // 更新计算结果return true; // 计算成功}// 格式化显示结果resultFormat(value: string): string {let reg: RegExp = (value.indexOf('.') > -1) ? new RegExp("/(\d)(?=(\d{3})+\.)/g") : new RegExp("/(\d)(?=(?:\d{3})+$)/g");return value.replace(reg, '$1,'); // 添加千位分隔符}// 从资源获取字符串resourceToString(resource: Resource): string {if (CheckEmptyUtil.isEmpty(resource)) {return '';}let result = '';try {result = getContext(this).resourceManager.getStringSync(resource.id); // 获取资源字符串} catch (error) {Logger.error('[CalculateModel] getResourceString fail: ' + JSON.stringify(error)) // 记录错误}return result; // 返回结果}// 格式化输入值formatInputValue() {let deepExpressions: Array<string> = [];this.deepCopy().forEach((item: string, index: number) => {deepExpressions[index] = this.resultFormat(item); // 格式化每个输入项});this.inputValue = deepExpressions.join(''); // 将格式化的项连接成字符串}
}

-THE END-

代码与视频教程

完整案例代码与视频教程请参见:

代码:Code-05-03.zip。

视频:《实现菜谱二级联动导航》。

相关文章:

制作我的计算器

1. 界面布局 新建项目 MyCalculator&#xff0c;开始布局。 2. 静态布局 代码如下&#xff1a; // etc/pages/Index.ets Entry Component struct Index {build() {Column() {/*** 运算区*/Column() {TextInput({ text: 12x13 }).height(100%).fontSize(32).enabled(false).f…...

如何查看 Ubuntu开机是否需要密码

要查看 Ubuntu 开机是否需要密码&#xff0c;可以通过以下方法进行判断&#xff1a; 1. 检查自动登录设置 图形界面操作&#xff1a; 进入系统设置&#xff08;Settings&#xff09;→ 用户账户&#xff08;User Accounts&#xff09;→ 解锁设置&#xff08;输入当前用户密码…...

今日行情明日机会——20250519

上证指数缩量收十字星&#xff0c;个股涨多跌少&#xff0c;这周反弹的概率比较大。 深证指数缩量调整&#xff0c;临近反弹&#xff0c;个股表现更好。 2025年5月19日涨停股主要行业方向分析 并购重组&#xff08;政策驱动资产整合&#xff09; • 涨停家数&#xff1a;16…...

【CodeBuddy 】从0到1,让网页导航栏变为摸鱼神器

【CodeBuddy 】从0到1&#xff0c;让网页导航栏变为摸鱼神器 我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 &#x1f31f;嗨&#xff0c;我是LucianaiB&#…...

PCL点云库点云数据处理入门系列教材目录(2025年5月更新....)

PCL点云库点云数据处理入门系列教材目录 基础阶段 第 1 讲&#xff1a;PCL库简介和安装&#xff08;Win10/11VS2019PCL 1.12.0&#xff09;第 2 讲&#xff1a;PCL库中点云基本知识和数据类型结构第 3 讲&#xff1a;PCL库中点云数据格式PCD和PLY及其输入输出&#xff08;IO&…...

同一颗太阳:Australia、Austria、Arab、Africa、Augustus、August、Aurora、Athena

我们来看一下下面这一堆单词&#xff1a; Australia n.澳大利亚&#xff1b;澳洲 Australian n.澳大利亚人 a.澳大利亚的 Austria n.奥地利 Austrian n.奥地利人 a.奥地利(人)的 Africa n.非洲 African n.非洲人* Arab a.阿拉伯的&#xff1b;阿拉伯人的 n.阿拉伯人(pl.Arabs)…...

用户账号及权限管理:企业安全的基石与艺术

在当今数字化时代,用户账号及权限管理已成为企业IT安全体系中不可或缺的核心组件。它不仅是保护敏感数据的第一道防线,更是确保业务运营效率和合规性的关键。本文将深入探讨用户账号及权限管理的重要性、最佳实践以及实施策略,助您构建一个安全、高效且灵活的访问控制体系。…...

存储系统03——数据缓冲evBuffer

存储系统03——数据缓冲evBuffer 数据缓冲evBuffer分段存储零拷贝线程安全 evbuffer 实例——存储系统事件触发 数据缓冲evBuffer evbuffer 是 Libevent 提供的一个高效内存缓冲区管理工具&#xff0c;用于存储和操作数据。它类似于一个动态增长的字节缓冲区&#xff0c;支持多…...

留给王小川的时间不多了

王小川&#xff0c;这位头顶“天才少年”光环的清华学霸、搜狗输入法创始人、中国互联网初代技术偶像&#xff0c;正迎来人生中最难啃的硬骨头。 他在2023年创立的百川智能&#xff0c;被称为“大模型六小虎”之一。今年4月&#xff0c;王小川在全员信中罕见地反思过去两年工作…...

Python海龟绘图-斗地主

#导入库 import random as r import turtle as t #数据 pk[红心A,红心2,红心3,红心4,红心5,红心6,红心7,红心8, 红心9,红心10,红心J,红心Q,红心K,黑桃A,黑桃2,黑桃3,黑桃4,黑桃5,黑桃6,黑桃7,黑桃8, 黑桃9,黑桃10,黑桃J, 黑桃Q,黑桃K,方块A,方块2,方块3,方块4,方块5,方块6,方块…...

一、内存调优

一、内存调优 什么是内存泄漏 监控Java内存的常用工具 内存泄露的常见场景 内存泄露的解决方案 内存泄露与内存溢出的区别 内存泄露&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收&…...

机器学习--特征工程具体案例

一、数据集介绍 sklearn库中的玩具数据集&#xff0c;葡萄酒数据集。在前两次发布的内容《机器学习基础中》有介绍。 1.1葡萄酒列标签名&#xff1a; wine.feature_names 结果&#xff1a; [alcohol, malic_acid, ash, alcalinity_of_ash, magnesium, total_phenols, flavanoi…...

Java-List集合类全面解析

Java-List集合类全面解析 前言一、List接口概述与核心特性1.1 List在集合框架中的位置1.2 List的核心特性1.3 常见实现类对比 二、ArrayList源码剖析与应用场景2.1 内部结构与初始化2.2 动态扩容机制2.3 性能特点与最佳实践 三、LinkedList 源码剖析与应用场景3.1 内部结构与节…...

在Cursor中启用WebStorm/IntelliJ风格快捷键

在Cursor中启用WebStorm/IntelliJ风格快捷键 方法一&#xff1a;使用预置快捷键方案 打开快捷键设置 Windows/Linux: Ctrl K → Ctrl SmacOS: ⌘ K → ⌘ S 搜索预设方案 在搜索框中输入keyboard shortcuts&#xff0c;选择Preferences: Open Keyboard Shortcuts (JSON) …...

32、跨平台咒语—— React Native初探

一、时空晶体架构&#xff08;核心原理&#xff09; 1. 量子组件桥接协议 // 原生组件映射 <View> → iOS UIView / Android ViewGroup <Text> → UILabel / TextView 魔法特性&#xff1a; • JavaScriptCore引擎&#xff1a;通过V8/Hermes引擎执行JS逻辑…...

无源探头衰减比与带宽特性的关联性研究

引言 在电子测量领域&#xff0c;无源探头作为示波器的重要附件&#xff0c;其性能参数直接影响测量结果的准确性。本文将从电路设计原理出发&#xff0c;深入分析衰减比与带宽这两个关键参数的相互关系&#xff0c;帮助工程师正确理解并选择适合的测量探头。 技术原理分析 …...

虚拟机的三个核心类加载器

虚拟机的三个核心类加载器 在Java虚拟机(JVM)中,类加载器(ClassLoader)负责将类的字节码加载到内存中,并生成对应的Class对象。以下是三个核心类加载器的详细说明: 1. 启动类加载器(Bootstrap ClassLoader) 职责: 加载Java核心类库(如java.lang、java.util等),位…...

国家互联网信息办公室关于发布第十一批深度合成服务算法备案信息的公告

wenz 根据《互联网信息服务深度合成管理规定》&#xff0c;现公开发布第十一批境内深度合成服务算法备案信息&#xff0c;具体信息可通过互联网信息服务算法备案系统&#xff08;https://beian.cac.gov.cn&#xff09;进行查询。任何单位或个人如有疑议&#xff0c;请发送邮件…...

深入理解动态规划:从斐波那契数列到最优子结构

引言 动态规划(Dynamic Programming, DP)是算法设计中一种非常重要的思想&#xff0c;广泛应用于解决各类优化问题。许多看似复杂的问题&#xff0c;通过动态规划的视角分析&#xff0c;往往能找到高效的解决方案。本文将系统介绍动态规划的核心概念&#xff0c;通过经典案例展…...

AT 指令详解:基于 MCU 的通信控制实战指南AT 指令详解

在 MCU&#xff08;单片机&#xff09;项目中&#xff0c;我们经常需要与各种通信模组&#xff08;GSM、Wi-Fi、蓝牙等&#xff09;交互。而这类模组通常都通过串口&#xff08;UART&#xff09;与 MCU 通信&#xff0c;控制它们的“语言”就是——AT 指令。 一、什么是 AT 指…...

初学c语言16(内存函数)

1.memcpy 形式&#xff1a; 功能&#xff1a;完成内存块拷贝&#xff08;所以可拷贝任何类型的数据&#xff09; 过程&#xff1a;从source开始拷贝num个字节的数据到destination指向的空间里 返回值&#xff1a;返回目标空间的起始地址 应用&#xff1a; 模拟实现&#xf…...

【git进阶】git rebase(变基)

文章目录 合并分支提交信息修改合并提交记录时间问题1时间问题2时间问题3git rebase有很多用武之地,我一一道来 合并分支 当多人协作同一个分支时,在提交我们自己版本之前,我们会先用git pull获取远端最新的版本。但是 git pull = git fetch + git mergegit merge是一个非…...

Pytorch---view()函数

在PyTorch中&#xff0c;view函数是一个极为重要的张量操作函数&#xff0c;其主要功能是对张量的形状进行重塑&#xff0c;与此同时会尽力维持张量中元素的总数不变。 1. 基本功能与语法 view函数的主要作用是改变张量的维度和大小&#xff0c;不过要保证重塑前后张量的元素…...

AI Agent开发第71课-一个完善的可落地企业AI Agent全架构

开篇 在之前的若干篇章里我们大量叙述了DIFY AI工作流、重排序、提示词重写、文档chunk、AI翻页、各种高级RAG应用以及AI Agent案例甚至全代码的输出。 目的,就是为了帮助大家认识到这么一件事,那就是: 当前AI主要还是在被叫好却不卖座,很多人(包括我身边的太多大厂)去…...

Prompt、Agent、MCP关系

AI基础概念概述 链接: https://www.bilibili.com/video/BV1aeLqzUE6L?t419.4 Agent&#xff08;智能体&#xff09;&#xff1a;智能体是能够执行特定任务的程序或实体&#xff0c;它可以根据环境变化调整自身行为。 MCP&#xff08;多通道协议&#xff09;&#xff1a;MCP是…...

人工智能100问☞第27问:神经网络与贝叶斯网络的关系?

神经网络与贝叶斯网络是两种互补的智能模型:神经网络通过多层非线性变换从数据中学习复杂模式,擅长大规模特征提取和预测,而贝叶斯网络基于概率推理建模变量间的条件依赖关系,擅长处理不确定性和因果推断。两者的融合(如贝叶斯神经网络)结合了深度学习的表征能力与概率建…...

Vue-样式绑定-style

样式绑定-style 对象写法数组写法 对象写法 :style"{fontSize: x}", x是动态值 &#xff08;{fontSize: x}是样式对象&#xff09; 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>…...

Python60日基础学习打卡D30

回顾&#xff1a; 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; # 直接导入 from random import randint print(randint(1, 10)) # 导入自定义库 import module m…...

el-upload图片设置了url不显示问题

引用&#xff1a;可以使用Image组件测试url是否可以用 此时不显示图片&#xff0c;因为打印后提示图片加载失败 此时图片显示 使用的base64的格式。但要注意在生成的base64码前要加上data:image/png;base64的内容才可以赋值给url...

学习黑客PowerShell的历史、架构与工作原理深度解析

PowerShell的历史、架构与工作原理深度解析 &#x1f50d; 作者: 海尔辛 | 发布时间: 2025-05-19 12:28:44 UTC 1. PowerShell的历史演变 &#x1f4dc; &#x1f539; 诞生背景与起源 PowerShell的诞生源于微软解决Windows管理工具碎片化问题的需求。在PowerShell出现之前…...

视觉-和-语言导航的综述:任务、方法和未来方向

22年6月来自UC Santa Cruz、澳大利亚的阿德莱德大学和 USC 的论文“Vision-and-Language Navigation: A Survey of Tasks, Methods, and Future Directions”。 人工智能研究的一个长期目标是构建能够用自然语言与人类交流、感知环境并执行现实世界任务的智体。视觉与语言导航…...

JUC入门(四)

ReadWriteLock 代码示例&#xff1a; package com.yw.rw;import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteDemo {public static void main(String[] args) {MyCache myCache new MyCache…...

day 21 常见降维算法

一、无监督降维 定义&#xff1a;这类算法在降维过程中不使用任何关于数据样本的标签信息&#xff08;比如类别标签、目标值等&#xff09;。它们仅仅根据数据点本身的分布、方差、相关性、局部结构等特性来寻找低维表示。 输入&#xff1a;只有特征矩阵 X。 目标&#xff1a…...

代理IP高可用性与稳定性方案:负载均衡、节点健康监测与智能切换策略

一、负载均衡策略&#xff1a;动态分配与场景适配 多算法协同调度 轮询与加权轮询&#xff1a;适用于定时数据采集等低频任务&#xff0c;通过静态IP池按顺序分配请求&#xff0c;避免单一节点过载。例如&#xff0c;静态IP池可支持电商商品价格监控&#xff0c;按固定周期切换…...

C语言:在操作系统中,链表有什么应用?

在操作系统中&#xff0c;链表是一种重要的数据结构&#xff0c;凭借其灵活的内存管理和高效的插入/删除特性&#xff0c;被广泛应用于多个核心模块。以下是其主要应用场景及详细说明&#xff1a; 1. 内存管理&#xff1a;空闲内存块管理 应用场景&#xff1a;操作系统需要管…...

RocketMQ

一、引言 Message Queue&#xff08;消息 队列&#xff09;&#xff0c;从字⾯上理解&#xff1a;⾸先它是⼀个队列。FIFO先进先出的数据结构——队列。消息队列就是所谓的存放消息的队列。 消息队列解决的不是存放消息的队列的⽬的&#xff0c;解决的是通信问题。 …...

学习BI---QuickBI介绍

BI是什么 BI 数据仓库&#xff08;存数据&#xff09; OLAP&#xff08;多维分析&#xff09; 数据挖掘&#xff08;找规律&#xff09; 可视化&#xff08;图表/看板&#xff09; 人话解释就是把企业里乱七八糟的数据变成老板能看懂的报告&#xff0c;帮他们做更聪明的决…...

【数据结构】AVL树的实现

文章目录 1. AVL 的概念2. AVL 树的实现2.1 AVL 树的结构2.2 AVL 树的插入2.2.1 AVL 树插入一个值的大致过程2.2.2 平衡因子更新 2.3 旋转2.3.1 旋转的原则2.3.2 右单旋2.3.3 左单旋2.3.4 左右双旋2.3.5 右左双选 2.4 AVL 树的查找2.5 AVL 树平衡检测 1. AVL 的概念 AVL树是最…...

基于Zynq SDK的LWIP UDP组播开发实战指南

一、为什么选择LWIP组播? 在工业控制、智能安防、物联网等领域,一对多的高效数据传输需求日益增长。Zynq-7000系列SoC凭借其ARM+FPGA的独特架构,结合LWIP轻量级网络协议栈,成为嵌入式网络开发的理想选择。本文将带您实现: LWIP组播配置全流程动态组播组切换技术零拷贝数据…...

【esp32 控制台】-命令

文章目录 1 esp32控制台简介2 控制台命令编程2.1 控制台配置交互设备初始化控制台初始化等待命令输入 2.2 命令实现2.2.1 命令注册 踩坑记录 1 esp32控制台简介 可以通过idf.py monitor调出idf的控制台&#xff0c;结束控制台用ctrl ]。 esp32的控制台就像linux中的shell一样…...

DApp开发全流程解析:模式设计、功能参考与合约管理实践

DApp开发全流程解析&#xff1a;模式设计、功能参考与合约管理实践 引言&#xff1a;Web3.0时代的DApp开发范式 随着区块链技术的成熟&#xff0c;DApp&#xff08;去中心化应用&#xff09;已成为Web3.0生态的核心载体。截至2025年&#xff0c;全球DApp数量突破10万&#xf…...

I/O多路复用:poll与epoll

一、select/poll与epoll对比 核心区别 特性select/pollepoll内核数据结构数组&#xff08;线性结构&#xff09;红黑树&#xff08;存储监听的fd&#xff09;内存拷贝每次调用需将fd列表从用户态拷贝到内核态仅在注册fd时拷贝一次&#xff08;epoll_ctl&#xff09;内核事件检…...

【调制识别】PGD攻击中参数的含义

在PGD&#xff08;Projected Gradient Descent&#xff09;对抗攻击中&#xff0c;代码如下&#xff1a; # 定义PGD对抗样本生成类 class AttackPGD(nn.Module): def __init__(self, model, config):super(AttackPGD, self).__init__()self.model model …...

设备数据看板助力自动化工厂实现生产智能精细化管理

工厂数字化转型需要实现自动化设备生产现场可视化、设备系统间的互联互通&#xff0c;以及数据的智能决策。然而&#xff0c;当前许多制造企业仍面临着传统单机设备同质化严重、数字化服务能力不足、售后成本高企、系统集成效率低下等挑战。企业如何通过自动化装备看板和实时数…...

单点登录是是什么?具体流程是什么?

SSO⼀般都需要⼀个独⽴的认证中⼼&#xff08;passport&#xff09;&#xff0c;⼦系统的登录均得通过passport&#xff0c;⼦系统本⾝将不参与登录操作&#xff0c;当⼀个系统成功登录以后&#xff0c;passport将会颁发⼀个令牌给各个⼦系统&#xff0c;⼦系统可以拿着令牌会获…...

什么业务需要用到waf

Web应用防火墙&#xff08;Web Application Firewall&#xff0c;简称WAF&#xff09;主要用于保护Web应用程序免受各种网络攻击&#xff0c;以下是需要用到WAF的业务类型及具体场景&#xff1a; 一、电子商务业务 业务特点&#xff1a;涉及用户注册、登录、支付等敏感操作&a…...

java中的Servlet4.x详解

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Servlet 4.x 是 Java EE 8&#xff08;现 Jakarta EE&#xff09;规范的一部分&#xff0c;于 2017 年正式发布。作为 Servlet 技术的重大更…...

## Docker 中 Elasticsearch 启动失败:日志文件权限问题排查与解决

好的&#xff0c;这是一份关于你遇到的 Docker Elasticsearch 启动报错问题的笔记&#xff0c;包含问题描述、我的分析判断以及最终的解决方案&#xff0c;适合用于整理成文章。 Docker 中 Elasticsearch 启动失败&#xff1a;日志文件权限问题排查与解决 在使用 Docker部署 E…...

JavaScript:PC端特效--缓动动画

一、缓动效果原理 缓动动画就是让元素运动速度有所变化&#xff0c;最常见的就是让元素慢慢停下来 思路&#xff1a; 让盒子每次移动的距离慢慢变小&#xff0c;速度就会慢慢降下来核心算法&#xff1a;&#xff08;目标值-现在位置&#xff09;/10作为每次移动距离的步长停…...

【深度学习新浪潮】什么是多模态大模型?

多模态大模型是人工智能领域的前沿技术方向,它融合了多种数据模态(如文本、图像、语音、视频、传感器数据等),并通过大规模参数模型实现跨模态的联合理解与生成。简单来说,这类模型就像人类一样,能同时“看”“听”“读”“说”,并将不同信息关联起来,完成复杂任务。 …...