使用ts实现网页计算器工具,实现计算器相关功能,使用tsify进行项目编译,引入Browserify实现web界面中直接使用模块加载服务。
源码下载:点击下载
-
讲解视频
TS实战项目四:计算器项目创建
TS实战项目五:Browserify安装配置
-
B站视频
TS实战项目四:计算器项目创建
TS实战项目五:Browserify安装配置
-
西瓜视频
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进行扩展声明,实现相应的功能。
问题二: 直接给元素添加点击事件回调时,接口类型的问题。