效果:
<template>
<div class="calculator-container">
<div class="calculator">
<!-- 显示区域 -->
<div class="display">{{ formattedDisplay }}</div>
<!-- 按钮区域 -->
<div class="buttons">
<button @click="clear">C</button>
<button @click="appendParentheses">()</button>
<button @click="backspace">⌫</button>
<button @click="append('/')">÷</button>
<button @click="append('7')">7</button>
<button @click="append('8')">8</button>
<button @click="append('9')">9</button>
<button @click="append('*')">×</button>
<button @click="append('4')">4</button>
<button @click="append('5')">5</button>
<button @click="append('6')">6</button>
<button @click="append('-')">−</button>
<button @click="append('1')">1</button>
<button @click="append('2')">2</button>
<button @click="append('3')">3</button>
<button @click="append('+')">+</button>
<button @click="append('%')">%</button>
<button @click="append('0')">0</button>
<button @click="append('.')">.</button>
<button class="equals" @click="calculate">=</button>
</div>
</div>
</div>
</template>
<script setup lang="ts" >
import { ref, computed } from "vue";
import { evaluate } from "mathjs";
// 表达式和显示内容
const expression = ref("");
const display = ref("0");
// 格式化显示内容:将 * → ×,/ → ÷
const formattedDisplay = computed(() => {
return expression.value.replace(/\*/g, "×").replace(/\//g, "÷");
});
// 添加字符到表达式
function append(char:string) {
const expr = expression.value;
const lastChar = expr.slice(-1);
// 如果表达式为空,且输入非法起始字符(如 + - * / % )),则阻止
if (expr === "" && ["+", "-", "*", "/", "%", ")"].includes(char)) {
return;
}
// 特别允许:如果前一个是左括号,并且当前字符是 + - 数字 → 允许
if (lastChar === "(" && (["+", "-"].includes(char) || /\d/.test(char))) {
expression.value += char;
updateDisplay();
return;
}
// 特别阻止:如果前一个是左括号,并且当前字符是 * / × ÷ % ( . → 不允许
if (lastChar === "(" && ["*", "/", "×", "÷", "%", "(", "."].includes(char)) {
return;
}
// 阻止连续运算符(比如 ++, +*, -- 等)
const operators = ["+", "-", "*", "/", "×", "÷", "%"];
if (operators.includes(lastChar) && operators.includes(char)) {
return;
}
// 阻止非法插入:数字或右括号后插入左括号 (
if ((/[\d]/.test(lastChar) || lastChar === ")") && char === "(") {
return;
}
// 默认情况下允许添加字符(包括继续输入数字、小数点、括号等)
expression.value += char;
updateDisplay();
}
// 清除输入
function clear() {
expression.value = "";
updateDisplay();
}
// 删除最后一个字符
function backspace() {
expression.value = expression.value.slice(0, -1);
updateDisplay();
}
// 插入括号:智能判断插入 '(' 还是 ')'
function appendParentheses() {
const expr = expression.value;
const lastChar = expr.slice(-1);
// 第一步:统计所有未闭合的左括号数量
let openCount = 0;
for (let i = 0; i < expr.length; i++) {
if (expr[i] === '(') openCount++;
else if (expr[i] === ')') openCount--;
}
// 第二步:如果最后一个字符是 ( → 强制插入 )
if (lastChar === '(') {
expression.value += ')';
updateDisplay();
return;
}
// 第三步:如果有未闭合的左括号,允许插入 )
if (openCount > 0) {
expression.value += ')';
updateDisplay();
return;
}
// 第四步:如果前一个是数字或 ) → 不允许插入新的 (
if (expr && /[\d)]/.test(lastChar)) {
return;
}
// 第五步:没有未闭合的左括号,可以插入新的 (
expression.value += '(';
updateDisplay();
}
// 执行计算
function calculate() {
try {
let expr = expression.value.replace(/(\d+)%/g, "($1 / 100)");
expr = expr.replace(/\^/g, "**");
const result = evaluate(expr).toString();
expression.value = result;
updateDisplay();
} catch (e) {
display.value = "错误";
}
}
// 更新显示内容
function updateDisplay() {
display.value = formattedDisplay.value || "0";
}
</script>
<style scoped>
.calculator-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f4f4f4;
}
.calculator {
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 320px;
max-width: 100%;
}
.display {
width: 100%;
height: 60px;
font-size: 2rem;
text-align: right;
padding: 10px 15px;
margin-bottom: 15px;
background-color: #eaeaea;
border-radius: 8px;
overflow-x: auto;
white-space: nowrap;
}
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
.buttons button {
width: 100%;
height: 60px;
font-size: 1.2rem;
border: none;
border-radius: 8px;
background-color: #f0f0f0;
cursor: pointer;
transition: background-color 0.2s ease;
}
.buttons button:hover {
background-color: #ddd;
}
.buttons button:active {
background-color: #ccc;
}
/* 特殊按钮:等号 */
.buttons button.equals {
background-color: #ff9800 !important;
color: #000 !important;
font-weight: bold;
}
</style>