mirror of
http://36.133.248.69:3088/admin/RentWeAppFront.git
synced 2026-03-07 17:32:25 +08:00
241 lines
4.4 KiB
Vue
241 lines
4.4 KiB
Vue
<template>
|
||
<view class="calculator">
|
||
<customNavbar title="计算器" :showHome="true" />
|
||
|
||
<textarea class="display" auto-height v-model="expression" disabled />
|
||
|
||
<view class="keys">
|
||
<view
|
||
v-for="item in keys"
|
||
:key="item"
|
||
class="key"
|
||
:class="{
|
||
operator: operators.includes(item) || item === '%',
|
||
equal: item === '=',
|
||
control: ['C','⌫'].includes(item)
|
||
}"
|
||
@click="press(item)"
|
||
@touchstart="onTouchStart(item)"
|
||
@touchend="onTouchEnd"
|
||
>
|
||
{{ item }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
expression: '',
|
||
timer: null,
|
||
operators: ['+', '-', '*', '/'],
|
||
keys: [
|
||
'7','8','9','/',
|
||
'4','5','6','*',
|
||
'1','2','3','-',
|
||
'0','.','%','+',
|
||
'C','⌫','='
|
||
]
|
||
}
|
||
},
|
||
methods: {
|
||
press(key) {
|
||
const ops = this.operators
|
||
|
||
if (key === 'C') {
|
||
this.expression = ''
|
||
return
|
||
}
|
||
|
||
if (key === '⌫') {
|
||
return
|
||
}
|
||
|
||
if (key === '%') {
|
||
this.applyPercent()
|
||
return
|
||
}
|
||
|
||
if (key === '=') {
|
||
if (!this.expression) return
|
||
|
||
while (/[+\-*/.]$/.test(this.expression)) {
|
||
this.expression = this.expression.slice(0, -1)
|
||
}
|
||
|
||
if (!/^\d+(\.\d+)?([+\-*/]\d+(\.\d+)?)*$/.test(this.expression)) {
|
||
uni.showToast({ title: '格式错误', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
this.expression = this.calculate(this.expression)
|
||
return
|
||
}
|
||
|
||
if (ops.includes(key)) {
|
||
if (!this.expression) return
|
||
if (ops.includes(this.expression.slice(-1))) {
|
||
this.expression =
|
||
this.expression.slice(0, -1) + key
|
||
return
|
||
}
|
||
}
|
||
|
||
if (key === '.') {
|
||
const last = this.expression.split(/[+\-*/]/).pop()
|
||
if (last.includes('.')) return
|
||
}
|
||
|
||
this.expression += key
|
||
},
|
||
|
||
/* 长按删除 */
|
||
onTouchStart(key) {
|
||
if (key !== '⌫') return
|
||
|
||
this.expression = this.expression.slice(0, -1)
|
||
|
||
this.timer = setInterval(() => {
|
||
if (!this.expression) {
|
||
clearInterval(this.timer)
|
||
return
|
||
}
|
||
this.expression = this.expression.slice(0, -1)
|
||
}, 120)
|
||
},
|
||
|
||
onTouchEnd() {
|
||
clearInterval(this.timer)
|
||
this.timer = null
|
||
},
|
||
|
||
/* 百分号处理(核心) */
|
||
applyPercent() {
|
||
if (!this.expression) return
|
||
|
||
const match = this.expression.match(/(\d+(\.\d+)?)([+\-*/])?(\d+(\.\d+)?)?$/)
|
||
if (!match) return
|
||
|
||
const base = parseFloat(match[1])
|
||
const op = match[3]
|
||
const value = match[4] ? parseFloat(match[4]) : null
|
||
|
||
let percentValue
|
||
|
||
if (!op) {
|
||
percentValue = base / 100
|
||
this.expression = percentValue.toString()
|
||
return
|
||
}
|
||
|
||
if (op === '+' || op === '-') {
|
||
percentValue = base * (value / 100)
|
||
} else {
|
||
percentValue = value / 100
|
||
}
|
||
|
||
this.expression =
|
||
this.expression.slice(0, match.index) +
|
||
base +
|
||
op +
|
||
percentValue
|
||
},
|
||
|
||
/* 计算(无 eval) */
|
||
calculate(exp) {
|
||
const nums = exp.split(/[\+\-\*\/]/).map(Number)
|
||
const ops = exp.match(/[\+\-\*\/]/g) || []
|
||
|
||
for (let i = 0; i < ops.length; ) {
|
||
if (ops[i] === '*' || ops[i] === '/') {
|
||
const r =
|
||
ops[i] === '*'
|
||
? nums[i] * nums[i + 1]
|
||
: nums[i] / nums[i + 1]
|
||
|
||
nums.splice(i, 2, r)
|
||
ops.splice(i, 1)
|
||
} else {
|
||
i++
|
||
}
|
||
}
|
||
|
||
let result = nums[0]
|
||
for (let i = 0; i < ops.length; i++) {
|
||
result =
|
||
ops[i] === '+'
|
||
? result + nums[i + 1]
|
||
: result - nums[i + 1]
|
||
}
|
||
|
||
return Number(result.toFixed(10)).toString()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.calculator {
|
||
padding: 30rpx;
|
||
padding-top: 175rpx;
|
||
background: #f7f8fa;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.display {
|
||
font-size: 70rpx;
|
||
font-weight: 600;
|
||
min-height: 400rpx;
|
||
width: 95%;
|
||
border-radius: 12rpx;
|
||
background: #fff;
|
||
padding: 0 24rpx;
|
||
margin-bottom: 30rpx;
|
||
box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.08);
|
||
}
|
||
|
||
.keys {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.key {
|
||
background: #ffffff;
|
||
text-align: center;
|
||
padding: 36rpx 0;
|
||
font-size: 70rpx;
|
||
font-weight: 500;
|
||
border-radius: 16rpx;
|
||
box-shadow: 0 10rpx 20rpx rgba(0,0,0,0.08);
|
||
}
|
||
|
||
.key:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
.operator {
|
||
background: #ffe8d9;
|
||
color: #ff7a18;
|
||
box-shadow: 0 10rpx 20rpx rgba(255,122,24,0.35);
|
||
font-size: 70rpx;
|
||
}
|
||
|
||
.control {
|
||
background: #ff4d4f;
|
||
color: #fff;
|
||
box-shadow: 0 10rpx 20rpx rgba(255,77,79,0.4);
|
||
}
|
||
|
||
.equal {
|
||
width: 215%;
|
||
background: linear-gradient(135deg, #4facfe, #00f2fe);
|
||
color: #fff;
|
||
font-size: 70rpx;
|
||
font-weight: 600;
|
||
box-shadow: 0 12rpx 28rpx rgba(79,172,254,0.5);
|
||
}
|
||
</style>
|