Files
vue-desktop/src/apps/calculator/Calculator.vue
2025-10-10 10:28:36 +08:00

344 lines
7.8 KiB
Vue
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.

<template>
<BuiltInApp app-id="calculator" title="计算器">
<div class="calculator">
<div class="display">
<input v-model="displayValue" type="text" readonly class="display-input" :class="{ 'error': hasError }">
</div>
<div class="buttons">
<!-- 第一行 -->
<button @click="clear" class="btn btn-clear">C</button>
<button @click="deleteLast" class="btn btn-operation"></button>
<button @click="appendOperation('/')" class="btn btn-operation">÷</button>
<button @click="appendOperation('*')" class="btn btn-operation">×</button>
<!-- 第二行 -->
<button @click="appendNumber('7')" class="btn btn-number">7</button>
<button @click="appendNumber('8')" class="btn btn-number">8</button>
<button @click="appendNumber('9')" class="btn btn-number">9</button>
<button @click="appendOperation('-')" class="btn btn-operation">-</button>
<!-- 第三行 -->
<button @click="appendNumber('4')" class="btn btn-number">4</button>
<button @click="appendNumber('5')" class="btn btn-number">5</button>
<button @click="appendNumber('6')" class="btn btn-number">6</button>
<button @click="appendOperation('+')" class="btn btn-operation">+</button>
<!-- 第四行 -->
<button @click="appendNumber('1')" class="btn btn-number">1</button>
<button @click="appendNumber('2')" class="btn btn-number">2</button>
<button @click="appendNumber('3')" class="btn btn-number">3</button>
<button @click="calculate" class="btn btn-equals" rowspan="2">=</button>
<!-- 第五行 -->
<button @click="appendNumber('0')" class="btn btn-number btn-zero">0</button>
<button @click="appendNumber('.')" class="btn btn-number">.</button>
</div>
</div>
</BuiltInApp>
</template>
<script setup lang="ts">
import { ref, onMounted, inject } from 'vue'
import BuiltInApp from '../components/BuiltInApp.vue'
import type { SystemServiceIntegration } from '@/services/SystemServiceIntegration'
// 直接获取系统服务 - 无需通过SDK
const systemService = inject<SystemServiceIntegration>('systemService')
const displayValue = ref('0')
const hasError = ref(false)
const lastResult = ref<number | null>(null)
const shouldResetDisplay = ref(false)
// 直接使用系统存储服务保存历史记录
const saveHistory = async (expression: string, result: string) => {
try {
if (systemService) {
const resourceService = systemService.getResourceService()
const history = await resourceService.getStorage('calculator', 'history') || []
history.push({
expression,
result,
timestamp: new Date().toISOString()
})
// 只保存最近30条记录
if (history.length > 30) {
history.shift()
}
await resourceService.setStorage('calculator', 'history', history)
}
} catch (error) {
console.error('保存历史记录失败:', error)
}
}
// 移除事件服务相关代码
// // 直接使用事件服务发送通知
// const showNotification = (message: string) => {
// if (systemService) {
// const eventService = systemService.getEventService()
// eventService.sendMessage('calculator', 'user-interaction', {
// type: 'notification',
// message,
// timestamp: new Date()
// })
// }
// }
// 添加数字
const appendNumber = (num: string) => {
hasError.value = false
if (shouldResetDisplay.value) {
displayValue.value = '0'
shouldResetDisplay.value = false
}
if (num === '.') {
if (!displayValue.value.includes('.')) {
displayValue.value += num
}
} else {
if (displayValue.value === '0') {
displayValue.value = num
} else {
displayValue.value += num
}
}
}
// 添加运算符
const appendOperation = (op: string) => {
hasError.value = false
shouldResetDisplay.value = false
const lastChar = displayValue.value.slice(-1)
const operations = ['+', '-', '*', '/']
// 如果最后一个字符是运算符,替换它
if (operations.includes(lastChar)) {
displayValue.value = displayValue.value.slice(0, -1) + op
} else {
displayValue.value += op
}
}
// 计算结果
const calculate = async () => {
try {
hasError.value = false
let expression = displayValue.value
.replace(/×/g, '*')
.replace(/÷/g, '/')
// 简单的表达式验证
if (/[+\-*/]$/.test(expression)) {
return // 以运算符结尾,不计算
}
const originalExpression = displayValue.value
const result = eval(expression)
if (!isFinite(result)) {
throw new Error('除零错误')
}
displayValue.value = result.toString()
lastResult.value = result
shouldResetDisplay.value = true
// 保存历史记录
await saveHistory(originalExpression, result.toString())
// 移除事件服务相关代码
// // 发送通知
// showNotification(`计算结果: ${result}`)
} catch (error) {
hasError.value = true
displayValue.value = '错误'
setTimeout(() => {
clear()
}, 1000)
}
}
// 清空
const clear = () => {
displayValue.value = '0'
hasError.value = false
lastResult.value = null
shouldResetDisplay.value = false
}
// 删除最后一个字符
const deleteLast = () => {
hasError.value = false
if (shouldResetDisplay.value) {
clear()
return
}
if (displayValue.value.length > 1) {
displayValue.value = displayValue.value.slice(0, -1)
} else {
displayValue.value = '0'
}
}
// 键盘事件处理
const handleKeyboard = (event: KeyboardEvent) => {
event.preventDefault()
const key = event.key
if (/[0-9.]/.test(key)) {
appendNumber(key)
} else if (['+', '-', '*', '/'].includes(key)) {
appendOperation(key)
} else if (key === 'Enter' || key === '=') {
calculate()
} else if (key === 'Escape' || key === 'c' || key === 'C') {
clear()
} else if (key === 'Backspace') {
deleteLast()
}
}
onMounted(() => {
// 添加键盘事件监听
document.addEventListener('keydown', handleKeyboard)
})
</script>
<style scoped>
.calculator {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 320px;
margin: 20px auto;
}
.display {
margin-bottom: 20px;
}
.display-input {
width: 100%;
height: 80px;
font-size: 32px;
text-align: right;
padding: 0 20px;
border: 2px solid #e0e0e0;
border-radius: 8px;
background: #f8f9fa;
color: #333;
outline: none;
font-family: 'Courier New', monospace;
box-sizing: border-box;
}
.display-input.error {
color: #e74c3c;
border-color: #e74c3c;
}
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(5, 1fr);
gap: 12px;
height: 320px;
}
.btn {
border: none;
border-radius: 8px;
font-size: 20px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
outline: none;
display: flex;
align-items: center;
justify-content: center;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.btn:active {
transform: translateY(0);
}
.btn-number {
background: #ffffff;
color: #333;
border: 2px solid #e0e0e0;
}
.btn-number:hover {
background: #f8f9fa;
border-color: #007bff;
}
.btn-operation {
background: #007bff;
color: white;
}
.btn-operation:hover {
background: #0056b3;
}
.btn-clear {
background: #dc3545;
color: white;
}
.btn-clear:hover {
background: #c82333;
}
.btn-equals {
background: #28a745;
color: white;
grid-row: span 2;
}
.btn-equals:hover {
background: #218838;
}
.btn-zero {
grid-column: span 2;
}
/* 响应式设计 */
@media (max-width: 400px) {
.calculator {
margin: 10px;
padding: 15px;
}
.display-input {
height: 60px;
font-size: 24px;
}
.buttons {
height: 280px;
gap: 8px;
}
.btn {
font-size: 16px;
}
}
</style>