mirror of
http://36.133.248.69:3088/admin/RentWeAppFront.git
synced 2026-03-07 17:32:25 +08:00
完成大体功能和样式
This commit is contained in:
240
pages-biz/tool/calculator.vue
Normal file
240
pages-biz/tool/calculator.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<view class="calculator">
|
||||
<customNavbar title="计算器" :showHome="true" />
|
||||
|
||||
<input class="display" 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 {
|
||||
height: 120rpx;
|
||||
font-size: 44rpx;
|
||||
font-weight: 600;
|
||||
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: 36rpx;
|
||||
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: 40rpx;
|
||||
}
|
||||
|
||||
.control {
|
||||
background: #ff4d4f;
|
||||
color: #fff;
|
||||
box-shadow: 0 10rpx 20rpx rgba(255,77,79,0.4);
|
||||
}
|
||||
|
||||
.equal {
|
||||
grid-column: span 4;
|
||||
background: linear-gradient(135deg, #4facfe, #00f2fe);
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 12rpx 28rpx rgba(79,172,254,0.5);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user