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

350 lines
8.8 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.
import { config } from '../../config';
// 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,
showHelp: false,
showSettingsMenu: false,
historyList: [] as Array<{expression: string, result: string}>
},
onShareAppMessage() {
return config.share;
},
onShareTimeline() {
return config.share;
},
toggleHelp() {
this.setData({
showHelp: !this.data.showHelp,
showSettingsMenu: false
});
},
toggleSettings() {
this.vibrate();
this.setData({ showSettingsMenu: !this.data.showSettingsMenu });
},
closeSettings() {
if (this.data.showSettingsMenu) {
this.setData({ showSettingsMenu: false });
}
},
onLoad() {
// 开启分享菜单
wx.showShareMenu({
withShareTicket: false,
menus: ['shareAppMessage', 'shareTimeline']
});
// 从本地存储恢复历史记录
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
});
},
switchMode(e: any) {
const mode = e.currentTarget.dataset.mode;
const isSci = (mode === 'scientific');
if (this.data.isScientific !== isSci) {
this.vibrate();
this.setData({ isScientific: isSci });
}
},
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.vibrate();
this.setData({
showHistory: !this.data.showHistory,
showSettingsMenu: false
});
},
clearHistory() {
this.vibrate();
wx.removeStorageSync('CALC_HISTORY');
this.setData({ historyList: [] });
},
useHistoryResult(e: any) {
this.vibrate();
const result = e.currentTarget.dataset.result;
this.setData({
displayValue: result,
waitingForSecondOperand: false, // Treat as a fresh input that can be operated on
showHistory: false
});
},
copyResult() {
this.vibrate();
const { displayValue } = this.data;
wx.setClipboardData({
data: displayValue,
success: () => {
// wx.hideToast(); // 隐藏系统默认的"内容已复制"提示
// wx.showToast({
// title: '复制成功',
// icon: 'success'
// });
},
fail: (err) => {
console.error('复制失败:', err);
// 隐私协议未声明或用户拒绝会导致复制失败 (errno 112)
if (err.errno === 112 || (err.errMsg && err.errMsg.indexOf('privacy') > -1)) {
wx.showModal({
title: '提示',
content: '无法自动复制,请长按显示区域手动复制',
showCancel: false,
confirmText: '知道了'
});
}
}
});
}
});