Files
youlegames/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts

281 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// pages/calculator/calculator.ts
Page({
data: {
displayValue: '0',
history: '',
operator: null as string | null,
firstOperand: null as number | null,
waitingForSecondOperand: false,
isScientific: false,
showHistory: false,
historyList: [] as Array<{expression: string, result: string}>
},
onLoad() {
// 从本地存储恢复历史记录
const saved = wx.getStorageSync('CALC_HISTORY');
if (saved && Array.isArray(saved)) {
this.setData({ historyList: saved });
}
},
vibrate() {
wx.vibrateShort({ type: 'light' }).catch(() => {});
},
preventTouchMove() {
// 阻止历史面板底层滚动
},
toggleMode() {
this.vibrate();
this.setData({
isScientific: !this.data.isScientific
});
},
onDigit(e: any) {
this.vibrate();
const digit = e.currentTarget.dataset.digit;
const { displayValue, waitingForSecondOperand } = this.data;
// 科学模式允许更多数字
const limit = 15;
if (!waitingForSecondOperand && displayValue.length >= limit) {
return;
}
if (waitingForSecondOperand) {
this.setData({
displayValue: digit,
waitingForSecondOperand: false
});
} else {
this.setData({
displayValue: displayValue === '0' ? digit : displayValue + digit
});
}
},
onDot() {
this.vibrate();
const { displayValue, waitingForSecondOperand } = this.data;
if (waitingForSecondOperand) {
this.setData({
displayValue: '0.',
waitingForSecondOperand: false
});
} else if (displayValue.indexOf('.') === -1) {
this.setData({
displayValue: displayValue + '.'
});
}
},
onClear() {
this.vibrate();
this.setData({
displayValue: '0',
history: '',
operator: null,
firstOperand: null,
waitingForSecondOperand: false
});
},
onDelete() {
this.vibrate();
const { displayValue } = this.data;
if (displayValue.length > 1) {
this.setData({
displayValue: displayValue.slice(0, -1)
});
} else {
this.setData({ displayValue: '0' });
}
},
onToggleSign() {
this.vibrate();
const { displayValue } = this.data;
const newValue = parseFloat(displayValue) * -1;
this.setData({
displayValue: String(newValue)
});
},
onConstant(e: any) {
this.vibrate();
const type = e.currentTarget.dataset.const;
let val = 0;
if (type === 'pi') val = Math.PI;
if (type === 'e') val = Math.E;
this.setData({
displayValue: String(val).slice(0, 10),
waitingForSecondOperand: true // 下次输入将开启新数字,或者作为操作数
});
},
onMathFunc(e: any) { // 单操作数立即计算
this.vibrate();
const func = e.currentTarget.dataset.func;
let val = parseFloat(this.data.displayValue);
let result = 0;
let desc = '';
switch(func) {
case 'sin':
// 默认转为弧度计算 (度数 * PI / 180)
result = Math.sin(val * Math.PI / 180);
desc = `sin(${val})`;
break;
case 'cos':
result = Math.cos(val * Math.PI / 180);
desc = `cos(${val})`;
break;
case 'tan':
result = Math.tan(val * Math.PI / 180);
desc = `tan(${val})`;
break;
case 'log':
result = Math.log10(val);
desc = `log(${val})`;
break;
case 'ln':
result = Math.log(val);
desc = `ln(${val})`;
break;
case 'sqrt':
result = Math.sqrt(val);
desc = `√(${val})`;
break;
case 'pow2':
result = Math.pow(val, 2);
desc = `sqr(${val})`;
break;
case 'percent':
result = val / 100;
desc = `${val}%`;
break;
}
// 格式化结果
const formatted = parseFloat(result.toPrecision(12));
this.setData({
displayValue: String(formatted),
history: desc,
waitingForSecondOperand: true // 计算完后,下次输入数字是新数字
});
this.addToHistory(desc, String(formatted));
},
onOperator(e: any) {
this.vibrate();
const nextOperator = e.currentTarget.dataset.op;
const { displayValue, operator, firstOperand } = this.data;
const inputValue = parseFloat(displayValue);
if (operator && this.data.waitingForSecondOperand) {
this.setData({
operator: nextOperator,
// 更新历史记录中的符号
history: `${firstOperand} ${nextOperator}`
});
return;
}
let newFirstOperand = firstOperand;
if (firstOperand == null) {
newFirstOperand = inputValue;
} else if (operator) {
const result = this.performCalculation(operator, firstOperand, inputValue);
newFirstOperand = result;
this.setData({
displayValue: String(result),
});
}
this.setData({
firstOperand: newFirstOperand,
waitingForSecondOperand: true,
operator: nextOperator,
history: `${newFirstOperand} ${nextOperator}`
});
},
onEqual() {
this.vibrate();
const { displayValue, operator, firstOperand } = this.data;
const inputValue = parseFloat(displayValue);
if (operator && firstOperand != null) {
const result = this.performCalculation(operator, firstOperand, inputValue);
const expression = `${firstOperand} ${operator} ${inputValue}`;
this.setData({
displayValue: String(result),
firstOperand: null,
operator: null,
waitingForSecondOperand: true,
history: `${firstOperand} ${operator} ${inputValue} =`
});
this.addToHistory(expression, String(result));
}
},
performCalculation(op: string, first: number, second: number): number {
let result = 0;
switch(op) {
case '+': result = first + second; break;
case '-': result = first - second; break;
case '×': result = first * second; break;
case '÷': result = first / second; break;
default: return second;
}
const precision = 10000000000;
return Math.round(result * precision) / precision;
},
// 历史记录相关方法
addToHistory(expression: string, result: string) {
const list = this.data.historyList.slice();
list.unshift({ expression, result });
// 最多保留50条
if (list.length > 50) list.length = 50;
this.setData({ historyList: list });
wx.setStorageSync('CALC_HISTORY', list);
},
toggleHistory() {
this.setData({ showHistory: !this.data.showHistory });
},
clearHistory() {
this.setData({ historyList: [] });
wx.removeStorageSync('CALC_HISTORY');
},
useHistoryResult(e: any) {
const result = e.currentTarget.dataset.result;
this.setData({
displayValue: String(result),
showHistory: false,
waitingForSecondOperand: true
});
},
copyResult() {
wx.setClipboardData({
data: this.data.displayValue,
success: () => {
wx.showToast({ title: '已复制', icon: 'success', duration: 1000 });
}
});
}
})