使用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进行扩展声明,实现相应的功能。
问题二: 直接给元素添加点击事件回调时,接口类型的问题。