TS项目实战二:网页计算器

  使用ts实现网页计算器工具,实现计算器相关功能,使用tsify进行项目编译,引入Browserify实现web界面中直接使用模块加载服务。
  源码下载:点击下载

  1. 讲解视频

    TS实战项目四:计算器项目创建


    TS实战项目五:Browserify安装配置

  2. B站视频

    TS实战项目四:计算器项目创建


    TS实战项目五:Browserify安装配置

  3. 西瓜视频
    https://www.ixigua.com/7329331349171470899

一.知识点

1. tsify编译
2. tsconfig.json配置项
3. 模块定义及导入导出
4. 类定义
5. 参数属性
6. 存取器
7. 接口定义
8. 命名空间
9. 函数重载
10. 事件处理

二.效果预览

在这里插入图片描述

三.实现思路

  使用ui和逻辑控制分离的模式,实现ui绘制及计算数据的单独处理,自定义按钮、输入框等ui组件,并绘制到界面中;通过事件监听的方式实现按钮点击后对应的逻辑控制,入结果计算、结果展示等。

四.创建项目

1. 创建node项目,使用npm init命令,如下:

在这里插入图片描述

2. 安装ts库,npm install typescript --save:

在这里插入图片描述

3. .\node_modules.bin\tsc --init生成ts的项目配置文件,此处注意直接用vscode的powershell运行的时候会保存,请切换到cmd命令窗口执行命令:

在这里插入图片描述

4. 安装lite-server库,npm install lite-server,安装完毕后添加"start": "lite-server"指令,用于提供web服务:

在这里插入图片描述

5. 安装Browserify,npm install tsify,提供浏览器环境中进行模块加载器的相关支持,具体文档参见:https://github.com/smrq/tsify,可创建bs-config.js文件进行web服务的配置。

在这里插入图片描述

6. 安装后创建build.js文件,用于进行ts代码的编译处理:

在这里插入图片描述

7. 在package.json中添加编译指令:

"build-cli": "browserify -p tsify ./src/index.ts > ./dist/index.js",
"build-script": "node ./build.js > ./dist/index.js",

在这里插入图片描述

8. 创建dist文件夹,并创建index.html文件,实现web界面:

在这里插入图片描述

9. 创建后项目目录结构如下:

在这里插入图片描述

五.编码实现

1. tsconfig.json

{
	"compilerOptions": {
		/* Visit https://aka.ms/tsconfig to read more about this file */
		/* Projects */
		// "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
		// "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
		// "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
		// "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
		// "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
		// "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
		/* Language and Environment */
		"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
			// "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
			// "jsx": "preserve",                                /* Specify what JSX code is generated. */
			// "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */
			// "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
			// "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
			// "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
			// "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
			// "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
			// "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
			// "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
			// "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */
			/* Modules */
			"module": "commonjs", /* Specify what module code is generated. */
			"rootDir": "./", /* Specify the root folder within your source files. */
			// "moduleResolution": "node10",                     /* Specify how TypeScript looks up a file from a given module specifier. */
			// "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
			// "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
			// "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
			// "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
			// "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
			// "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
			// "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
			// "allowImportingTsExtensions": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
			// "resolvePackageJsonExports": true,                /* Use the package.json 'exports' field when resolving package imports. */
			// "resolvePackageJsonImports": true,                /* Use the package.json 'imports' field when resolving imports. */
			// "customConditions": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
			// "resolveJsonModule": true,                        /* Enable importing .json files. */
			// "allowArbitraryExtensions": true,                 /* Enable importing files with any extension, provided a declaration file is present. */
			// "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
			/* JavaScript Support */
			// "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
			// "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
			// "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
			/* Emit */
			// "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
			// "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
			// "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
			"sourceMap": true, /* Create source map files for emitted JavaScript files. */
			// "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
			// "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
			"outDir": "./dist", /* Specify an output folder for all emitted files. */
			// "removeComments": true,                           /* Disable emitting comments. */
			// "noEmit": true,                                   /* Disable emitting files from a compilation. */
			// "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
			// "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
			// "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
			// "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
			// "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
			// "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
			// "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
			// "newLine": "crlf",                                /* Set the newline character for emitting files. */
			// "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
			// "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
			// "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
			// "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
			// "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
			// "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
			/* Interop Constraints */
			// "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
			// "verbatimModuleSyntax": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
			// "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
			"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
			// "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
			"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
			/* Type Checking */
			"strict": true, /* Enable all strict type-checking options. */
			// "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
			// "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
			// "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
			// "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
			// "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
			// "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
			// "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
			// "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
			// "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
			// "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
			// "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
			// "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
			// "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
			// "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
			// "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
			// "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
			// "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
			// "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */
			/* Completeness */
			// "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
			"skipLibCheck": true /* Skip type checking all .d.ts files. */
	}
}

2. package.json

{
	"name": "demo2",
	"version": "1.0.0",
	"description": "",
	"main": "./src/index.ts",
	"scripts": {
		"build-cli": "browserify -p tsify ./src/index.ts > ./dist/index.js",
		"build-script": "node ./build.js > ./dist/index.js",
		"start": "lite-server"
	},
	"author": "",
	"license": "ISC",
	"dependencies": {
		"browserify": "^17.0.0",
		"lite-server": "^2.6.1",
		"tsify": "^5.0.4",
		"typescript": "^5.3.3"
	}
}

3. build.js

var browserify = require('browserify');
var tsify = require('tsify');

browserify()
	.add('./src/index.ts')
	.plugin(tsify, { noImplicitAny: true })
	.bundle()
	.on('error', function (error) { console.error(error.toString()); })
	.pipe(process.stdout);

4. bs-config.js

"use strict";
module.exports = {
	port: 8080,
	files: ['./dist/**/*.{html,css,js}'],
	server: {
		baseDir: './dist'
	}
}

5. index.html

<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<link rel="icon" href="/favicon.ico">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>计算器演示</title>
		<style>
			html,
			body {
			width: 100%;
			height: 100%;
			margin: 0px;
			padding: 0px;
			}

			#app {
			display: flex;
			justify-content: center;
			justify-items: center;
			align-items: center;
			height: 100vh;
			}

			#app .panel {
			margin: 0 auto;
			width: 300px;
			height: 410px;
			border: 1px solid #f8f8f8;
			background-color: #f5f5f5;
			box-shadow: 0 0 4px 4px #d7d7d7b5;
			border-radius: 5px;
			}

			#app .panel .result {
			font-size: 30px;
			font-weight: bold;
			text-align: right;
			height: 60px;
			line-height: 60px;
			padding: 10px;
			padding-bottom: 0px;
			user-select: none;
			}

			#app .panel .buttons .line {
			display: flex;
			height: 55px;
			line-height: 55px;
			width: 100%;
			}

			#app .panel .buttons .line .btnPanel {
			padding: 5px;
			flex: 1;
			}

			#app .panel .buttons .line .btnPanel button {
			width: 100%;
			height: 100%;
			border-radius: 5px;
			border: 1px solid #eee;
			cursor: pointer;
			background-color: #fff;
			user-select: none;
			font-size: 18px;
			}

			#app .panel .buttons .line .btnPanel button:hover {
			background-color: #00adff;
			color: #fff;
			}

			#app .panel .buttons .line .btnPanel button.eq {
			background-color: #00adff;
			color: #fff;
			border: 1px solid #00adff;
			border-radius: 5px;
			}
		</style>
	</head>

	<body>
		<div id="app"></div>
		<script src="./index.js"></script>
		<script>

		</script>
	</body>

</html>

6. src/index.ts

import ProcessFactory from './ProcessFactory';
import UI from './ui/UI';

const ui = new UI();


//初始化
ui.init(document.getElementById('app'), ProcessFactory);

7. src/ProcessFactory.ts

import BackProcess from './proecess/BackProcess';
import BaseProcess from './proecess/BaseProcess';
import CProcess from './proecess/CProcess';
import EqProcess from './proecess/EqProcess';
import FenProcess from './proecess/FenProcess';
import NumberProcess from './proecess/NumerProcess';
import OpratorProcess from './proecess/OpratorProcess';
import PercentProcess from './proecess/PercentProcess';
import PinFangProcess from './proecess/PinFangProcess';
import PointProcess from './proecess/PointProcess';
import SqtProcess from './proecess/SqtProcess';

/**
 * 处理器的工厂函数,根据不同的字符,生成不同的处理器
 */
export default function getProcess(char: string): BaseProcess | null {
    if (char == '0' || char == '1' || char == '2' || char == '3' || char == '4' || char == '5' || char == '6' || char == '7' || char == '8' || char == '9') {
        return new NumberProcess(char);
    }
    if (char == '.') {
        return new PointProcess(char);
    }
    if (char == '=') {
        return new EqProcess(char);
    }
    if (char == '+' || char == '-' || char == '*' || char == '/') {
        return new OpratorProcess(char);
    }
    if (char == 'C') {
        return new CProcess(char);
    }
    if (char == '←') {
        return new BackProcess(char);
    }
    if (char == '%') {
        return new PercentProcess(char);
    }
    if (char == '1/x') {
        return new FenProcess(char);
    }
    if (char == 'x^2') {
        return new PinFangProcess(char);
    }
    if (char == '根号') {
        return new SqtProcess(char);
    }

    return null;
}

8. src/ui/UI.ts

/**
 * 根容器
 */
const rootPanel: HTMLDivElement = document.createElement('div');

//展示结果
const resultPanel: HTMLDivElement = document.createElement('div');

//按钮容器
const buttonPanel: HTMLDivElement = document.createElement('div');

//按钮
const btns: string[][] = [
    ['%', 'CE', 'C', '←'],
    ['1/x', 'x^2', '根号', '/'],
    ['7', '8', '9', '*'],
    ['4', '5', '6', '-'],
    ['1', '2', '3', '+'],
    ['0', '.', '=']
];

//计算结果
let result = "0";

/**
 * UI工具
 */
export default class UI {
    /**
     * 初始化界面
     * @param root 
     */
    init(root: HTMLElement | null, getProcess: Function): HTMLDivElement {
        if (!root) {
            throw new Error('必须要指定根元素');
        }

        //设置类,控制样式
        rootPanel.className = 'panel';

        resultPanel.className = 'result';
        resultPanel.innerText = result;
        rootPanel.appendChild(resultPanel);

        buttonPanel.className = "buttons";

        btns.forEach(item => {
            let linePanel: HTMLDivElement = document.createElement('div');
            linePanel.className = 'line';
            item.forEach(text => {
                let buttonPanel: HTMLDivElement = document.createElement('div');
                buttonPanel.className = 'btnPanel';
                let button: HTMLButtonElement = document.createElement('button');
                button.innerText = text + "";
                if (text === '=') {
                    button.className = 'eq';
                }
                //附加按钮的标识,记录具体是什么内容
                button.setAttribute('content', text);
                let process = getProcess(text);
                if (process) {
                    button.onclick = () => {
                        result = process.process(result);
                        updateReslt();
                    };
                }
                buttonPanel.appendChild(button);
                linePanel.appendChild(buttonPanel);
            })
            buttonPanel.appendChild(linePanel);
        })

        rootPanel.appendChild(buttonPanel);


        //生成具体的元素
        root.appendChild(rootPanel);

        return rootPanel;
    }
}
/**
 * 更新计算结果
 */
function updateReslt() {
    resultPanel.innerText = result;
}

9. src/process/BaseProcess.ts

/**
 * 处理器
 */
export default interface BaseProcess {
    /**
     * 要处理的字符串
     */
    char: string;
    /**
     * 按钮点击后计算结构
     * @param value  按钮的值
     * @param result 计算原始的结果
     */
    process(result: string): string;
}

10. src/process/BackProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 删除按钮的处理
 */
export default class BackProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     *删除的处理
     * @param result 
     * @returns 
     */
    process(result: string): string {
        if (result.length > 0) {
            let result_ = result?.substring(0, result.length - 1);
            return result_ ? result_ : '0';
        }
        return '0';
    }
}

11. src/process/EqProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 等于号的处理
 */
export default class EqProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     *清空的处理
     * @param result 
     * @returns 
     */
    process(result?: string): string {
        return '0';
    }
}

12. src/process/EqProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 等于号的处理
 */
export default class EqProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 等于号的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return result;
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

13. src/process/FenProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 几分之几的处理
 */
export default class FenProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 几分之几的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return (1 / parseFloat(result)) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

14. src/process/NumerProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 计算数字型的按钮
 */
export default class NumberProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 计算结果,传入的是数字按钮
     * @param value 数字
     * @param result 现有的结果
     * @returns 合并后的值
     */
    process(result: string): string {
        if (this.char == '0') {
            if (parseFloat(result) == 0) {
                return '0';
            }
        }
        if (parseFloat(result) == 0 && result.indexOf('.') == -1) {
            return this.char;
        } else {
            return result + '' + this.char;
        }
    }
}

15. src/process/OpratorProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 实现操作符的处理
 */
export default class OpratorProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 操作符的处理,+,-,*,/
     * @param result 
     * @returns 合并后的结果
     */
    process(result: string): string {
        if (result.charAt(result.length - 1) == '+' || result.charAt(result.length - 1) == '-' || result.charAt(result.length - 1) == '*' || result.charAt(result.length - 1) == '/') {
            return result;
        }
        return '' + result + this.char;
    }
}

16. src/process/PercentProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 百分号的处理
 */
export default class PercentProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 百分号的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return (parseFloat(result) / 100) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

17. src/process/PinFangProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 几分之几的处理
 */
export default class PinFangProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 几分之几的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return (parseFloat(result) * parseFloat(result)) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

18. src/process/PointProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 实现操作符的处理
 */
export default class OpratorProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * .的处理
     * @param result 
     * @returns 合并后的结果
     */
    process(result: string): string {
        if (result.charAt(result.length - 1) == '.') {
            return result;
        }
        if (result.charAt(result.length - 1) == '+' || result.charAt(result.length - 1) == '-' || result.charAt(result.length - 1) == '*' || result.charAt(result.length - 1) == '/') {
            return result + '0.';
        }
        return result + '' + this.char;
    }
}

19. src/process/SqtProcess.ts

import BaseProcess from './BaseProcess';

/**
 * 几分之几的处理
 */
export default class SqtProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 几分之几的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return Math.sqrt(parseFloat(result)) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

六.遇到的问题

问题一: HTMLDivElement无法继承的问题。
  因为ts中HTMLDivElement是一个接口,没有声明类,因为是一个底层类型,不适合进行扩展。
 可以新建类实现一个此接口,但就需要实现所有的属性和接口的方法,不太显示;
 可以新建一个类,定义一个HTMLDivElement类型的数据,将需要封装的内容封装到新建的类中实现想要的效果;
 也可使用声明合并对HTMLDivElement进行扩展声明,实现相应的功能。
问题二: 直接给元素添加点击事件回调时,接口类型的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/375088.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

龙芯安装使用搜狗输入法

CPU&#xff1a;龙芯3A6000 操作系统&#xff1a;Loongnix 桌面主题&#xff1a;Cartoon 龙芯系统切换输入法的按键一般为&#xff1a;Ctrl空格。 1 安装搜狗输入法 进入Loongnix系统自带的龙芯应用合作社&#xff0c;寻找搜狗输入法&#xff0c;点击安装。 按下Ctrl空格&…

生成树技术华为ICT网络赛道

9.生成树 目录 9.生成树 9.1.生成树技术概述 9.2.STP的基本概念及工作原理 9.3.STP的基础配置 9.4.RSTP对STP的改进 9.5.生成树技术进阶 9.1.生成树技术概述 技术背景&#xff1a;二层交换机网络的冗余性与环路 典型问题1&#xff1a;广播风暴 典型问题2&#xff1a;MA…

C++多态_C++回顾

多态的概念 通俗的说多态就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的概念。 什么是多态 静态的多态 静态的多态即函数重载&#xff0c;编译时是参数匹配和函数名修饰规则。 动态的多态 运行时实现&#xff0c;跟指…

(篇九)MySQL常用内置函数

目录 ⌛数学函数 ⌛字符串函数 ⌛聚合函数 ⌛日期函数 &#x1f4d0;获取当前时间 &#x1f4d0;获取时间的某些内容 &#x1f4d0;​编辑 &#x1f4d0;格式化函数 &#x1f4cf;format类型&#xff1a; ⌛系统信息函数 ⌛类型转换函数 数学函数 字符串函数 聚合函…

《计算机网络简易速速上手小册》第6章:网络性能优化(2024 最新版)

文章目录 6.1 带宽管理与 QoS - 让你的网络不再拥堵6.1.1 基础知识6.1.2 重点案例&#xff1a;提高远程办公的视频会议质量实现步骤环境准备Python 脚本示例注意事项 6.1.3 拓展案例1&#xff1a;智能家居系统的网络优化实现思路Python 脚本示例 6.1.4 拓展案例2&#xff1a;提…

Go语言每日一练 ——链表篇(三)

传送门 牛客面试笔试必刷101题 ---------------- 链表中的节点每k个一组翻转 题目以及解析 题目 解题代码及解析 package main import _"fmt" import . "nc_tools" /** type ListNode struct{* Val int* Next *ListNode* }*//*** 代码中的类名、方…

矩阵的正定(positive definite)性质的作用

1. 定义 注意&#xff0c;本文中正定和半正定矩阵不要求是对称或Hermite的。 2. 性质 3. 作用 &#xff08;1&#xff09;Axb直接法求解 cholesky实对称正定矩阵求解复共轭对称正定矩阵求解LDL实对称非正定矩阵求解复共轭对称非正定矩阵求解复对称矩阵求解LU实非对称矩阵求解…

离线环境怎么下载python依赖包

公司内网环境无网络&#xff0c;运行自动化脚本需要安装python模块 1、脚本依赖包及其版本获取&#xff0c;记录在requirements.txt中 pipreqs ./script --encodingutf8 requirements.txt注意&#xff0c;这里是将./script 里的python模块自动扫描并写入到requirements.txt中…

QT学习日记 | 显示类控件

目录 前言 一、QLabel控件 1、属性介绍 2、实战演练 &#xff08;1&#xff09;文本格式属性 &#xff08;2&#xff09;图片属性 &#xff08;3&#xff09;对齐、换行、缩进、边距属性 &#xff08;4&#xff09;伙伴属性 二、QLCDNumber控件 1、属性介绍 2、实战…

图灵之旅--二叉树堆排序

目录 树型结构概念树的表示形式 二叉树概念特殊的二叉树二叉树性质二叉树的存储二叉树的遍历前中后序遍历 优先级队列(堆)概念 优先级队列的模拟实现堆的性质概念堆的存储方式堆的创建 堆常用接口介绍PriorityQueue的特性PriorityQueue常用接口介绍优先级队列的构造插入/删除/获…

闲聊电脑(5)装个 Windows(一)

​夜深人静&#xff0c;万籁俱寂&#xff0c;老郭趴在电脑桌上打盹&#xff0c;桌子上的小黄鸭和桌子旁的冰箱又开始窃窃私语…… 小黄鸭&#xff1a;冰箱大哥&#xff0c;上次说到硬盘分区和格式化&#xff0c;弄完之后&#xff0c;就该装系统了吧&#xff1f; 冰箱&#x…

确定问卷调查样本量

目录 1. 问卷数据类型1.1 定性数据&#xff06;定性分析1.2 定量数据&#xff06;定量分析 2. 确定初始样本容量&#xff1a;2.1 公式&#xff1a;2.2 Z值2.3 p2.4 e2.5 举例 3.调整初始样本容量&#xff1a;3.1 公式&#xff1a;3.2 结论就是 小结&#xff1a; 1. 问卷数据类型…

消息中间件之RocketMQ源码分析(七)

并行消费和顺序消费 ConsumeMessageService是一个通用的消费服务接口&#xff0c;它包含两个实现类org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService和 org.apache.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService&#xff0c;这两个…

学习并用好大模型

大模型是个好东西&#xff0c;学好并用好益处多多~ 1. 运用大模型服务我们的工作 运用大模型服务于工作&#xff0c;可以从以下几个方面着手&#xff1a; 知识管理与检索&#xff1a; 利用大模型强大的自然语言处理能力&#xff0c;建立企业内部的知识库系统。员工可以通过提问…

03-Java单例模式 ( Singleton Pattern )

单例模式 单例模式设计要点单例模式基础实现摘要实现范例 单例模式的几种实现方式1. 懒汉式&#xff0c;线程不安全2. 懒汉式&#xff0c;线程安全3. 饿汉式4. 双检锁/双重校验锁&#xff08;DCL&#xff0c;即 double-checked locking&#xff09;5. 登记式/静态内部类6. 枚举…

记录关于node接收并解析前端上传excel文件formData踩的坑

1.vue2使用插件formidable实现接收文件&#xff0c;首先接口不可以使用任何中间件&#xff0c;否则form.parse()方法不执行。 const express require(express) const multipart require(connect-multiparty); const testController require(../controller/testController)/…

【论文精读】多模态情感分析 —— VLP-MABSA

Vision-Language Pre-Training for Multimodal Aspect-Based Sentiment Analysis 本篇论文发表于ACL-2022 原文链接 https://arxiv.org/abs/2204.07955 源码 GitHub - NUSTM/VLP-MABSA 模态&#xff1a;图像文本 基于多模态方面的情感分析(MABSA)近年来越来越受到关注。然而&am…

【Power Automate】规避流程30天的运行时限(只针对审批流)

众所周知&#xff0c;Power Automate的流程最多只能运行30天&#xff0c;到点之后直接超时&#xff0c;如果我们有超时时间设置的比较长的审批就会很麻烦&#xff0c;可能我们把审批节点的超时时间都设置为25天&#xff0c;结果第一个审批人就把25天拉满了&#xff0c;那第二个…

SpringBoot实战第三天

今天主要完成了&#xff1a; 新增棋子分类 棋子分类列表 获取棋子分类详情 更新棋子分类 更新棋子分类和添加棋子分类_分组校验 新增棋子 新增棋子参数校验 棋子分类列表查询(条件分页) 先给出分类实体类 Data public class Category {private Integer id;//主键IDNot…

七月论文审稿GPT第2.5和第3版:分别微调GPT3.5、Llama2 13B以扩大对GPT4的优势

前言 自去年7月份我带队成立大模型项目团队以来&#xff0c;我司至今已有5个项目组&#xff0c;其中 第一个项目组的AIGC模特生成系统已经上线在七月官网第二项目组的论文审稿GPT则将在今年3 4月份对外上线发布第三项目组的RAG知识库问答第1版则在春节之前已就绪至于第四、第…
最新文章