Vue3＋Vuetify3で電卓を作る

# やったこと

仕事でVue+Vuetifyを使った経験があるので、@pagu_dayo (ぱぐ) さんが投稿された「[Reactで電卓を作る](https://qiita.com/pagu_dayo/items/27f3071057d604d2cf2a)」を移植してみました。
オンラインで動作確認できます。

https://play.vuetifyjs.com/#eNrFVm1v2jAQ/itHvkDXJSlt2ToW0KZtf2Bfxz4Yx4CHY0eOA2WI/76z8wpVO1WyVAkh39tzd49fLr+OQaFp/DXPo13JgmmQGJblghg2X0iAZBeSPAcqSFHMFgG6LQJncKaMcFlLKG/G8+9MGrItB4NBEqPYmjqEwmgu1+FSPSLQ8QiaFaUwcDolcd75p3zXRlAlykzWEY3HhY9W+ycOrsKlka2TVDojYhGAYY8G5Y+4/EIFp1sUVM40No2q+DUYDx4wPnnAePd/jCRGxrwSeO+h8IkHjA8eMMK3IHDsofBbDxh3HjCu34LAGw+FRx4wYg8Y+OswKBG0tK+wdwrxwWUmXJbGKNkl/2m1/QKc24vJ+1IS94aBFXBo4DqJe8MExYJqnhtA5LJ67HmWK23AjoEVnGClVQZDnENDa6RKFqYZEDPrMhoOrzpLzTOaRkcwRK8ZzpErmM3hWFVVhUY7IkoG17PaJ7Itf1PS4KyyfqcOsWXdYvaRjD40ywvYGTBcjPo6VyLiIpyhGxgxrZW+ejZ++MPaB67luppeSW4fLsu5RHCxGJTEFcM12+Ygqike4dxuW8HuQyL4Wk6BIgVMf66jnWc3bZuAlBe4hYcprAR7dL7gluFek3wK9r/W/ikLw1eHECu35J7hA7icIccTUVxYHNpKqD3qXfqziupT/VI5z0NnuOUcO53klW8Niie3xZ1DdRmi6kI2efY8NRsMvKkjATaMrzfYVU/VwI9bzQp7Dwv+l01B2ON23krv7l3kGd/4TNR9ZTVploRu11qVMsXtEUrbqB2T6ROmxl3SpdJod0mhUIKnZ/pQk5SXSHjDLUBO0hTzTuG2A2n3i0vBJQuXQtFtk5PLsOn/oQmpznF9doPT++Auuo/Gk8AuJtFt8PsfPr5Qgg==

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29070/98044a58-f91a-969a-7836-5ef77b929fec.png)](https://play.vuetifyjs.com/#eNrFVm1v2jAQ/itHvkDXJSlt2ToW0KZtf2Bfxz4Yx4CHY0eOA2WI/76z8wpVO1WyVAkh39tzd49fLr+OQaFp/DXPo13JgmmQGJblghg2X0iAZBeSPAcqSFHMFgG6LQJncKaMcFlLKG/G8+9MGrItB4NBEqPYmjqEwmgu1+FSPSLQ8QiaFaUwcDolcd75p3zXRlAlykzWEY3HhY9W+ycOrsKlka2TVDojYhGAYY8G5Y+4/EIFp1sUVM40No2q+DUYDx4wPnnAePd/jCRGxrwSeO+h8IkHjA8eMMK3IHDsofBbDxh3HjCu34LAGw+FRx4wYg8Y+OswKBG0tK+wdwrxwWUmXJbGKNkl/2m1/QKc24vJ+1IS94aBFXBo4DqJe8MExYJqnhtA5LJ67HmWK23AjoEVnGClVQZDnENDa6RKFqYZEDPrMhoOrzpLzTOaRkcwRK8ZzpErmM3hWFVVhUY7IkoG17PaJ7Itf1PS4KyyfqcOsWXdYvaRjD40ywvYGTBcjPo6VyLiIpyhGxgxrZW+ejZ++MPaB67luppeSW4fLsu5RHCxGJTEFcM12+Ygqike4dxuW8HuQyL4Wk6BIgVMf66jnWc3bZuAlBe4hYcprAR7dL7gluFek3wK9r/W/ikLw1eHECu35J7hA7icIccTUVxYHNpKqD3qXfqziupT/VI5z0NnuOUcO53klW8Niie3xZ1DdRmi6kI2efY8NRsMvKkjATaMrzfYVU/VwI9bzQp7Dwv+l01B2ON23krv7l3kGd/4TNR9ZTVploRu11qVMsXtEUrbqB2T6ROmxl3SpdJod0mhUIKnZ/pQk5SXSHjDLUBO0hTzTuG2A2n3i0vBJQuXQtFtk5PLsOn/oQmpznF9doPT++Auuo/Gk8AuJtFt8PsfPr5Qgg==)

# 準備

自分のPCで動作させたい場合は、Node.jsとnpmをインストールして vuetify プロジェクトを create しておきます。

https://www.google.com/search?q=node+npm+install

https://vuetifyjs.com/en/getting-started/installation/#installation

src/App.vue などが自動生成されます。

# 実装コード

コンポーネント指向設計的には電卓コンポーネントを再利用できるように src/components 配下に作成して src/App.vue から inport して使うのですが、今回はReactと比較できるように src/App.vue に直接書いてみました。

```App.vue
<template>
  <v-app class="App">
    <v-main>
      <h1>Dentaku!!!</h1>
      <p class="string-box">{{ result }}</p>
      <div class="column-box">
        <div class="row-box">
          <v-btn class="normal" text="7" @click="operate" />
          <v-btn class="normal" text="8" @click="operate" />
          <v-btn class="normal" text="9" @click="operate" />
          <v-btn class="normal" text="*" @click="operate" />
        </div>
        <div class="row-box">
          <v-btn class="normal" text="4" @click="operate" />
          <v-btn class="normal" text="5" @click="operate" />
          <v-btn class="normal" text="6" @click="operate" />
          <v-btn class="normal" text="-" @click="operate" />
        </div>
        <div class="row-box">
          <v-btn class="normal" text="1" @click="operate" />
          <v-btn class="normal" text="2" @click="operate" />
          <v-btn class="normal" text="3" @click="operate" />
          <v-btn class="normal" text="+" @click="operate" />
        </div>
        <div class="row-box">
          <v-btn class="normal" text="0" @click="operate" />
          <v-btn class="normal" text="." @click="operate" />
          <v-btn class="normal" text="/" @click="operate" />
          <v-btn class="normal" text="=" @click="calculate" />
        </div>
        <div class="row-box">
          <v-btn class="reset-button" text="Reset" @click="reset" />
        </div>
      </div>
    </v-main>
  </v-app>
</template>

<script setup>
  import { ref } from "vue";
  const result = ref("");
  const operate = ({ target }) => {
    result.value += target.textContent;
  };
  const calculate = () => {
    try {
      result.value = eval(result.value);
    } catch (error) {
      result.value = "Error!";
    }
  };
  const reset = () => {
    result.value = "";
  };
</script>

<style>
.App {
  text-align: center;
}

.column-box {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  flex-flow: column;
}

.row-box {
  display: flex;
  align-items: center;
  margin: 5px;
}

div.row-box > button.normal {
  width: 50px;
  height: 50px;
  margin: 1px;
  font-size: large;
}

.reset-button {
  width: 100px;
  height: 50px;
  margin: 1px;
  font-size: large;
}

.string-box {
  background-color: lavender;
  margin: 10px;
  border: 1px solid;
  border-radius: 5px;
  padding: 20px;
  display: inline-block;
  min-width: 180px;
}
</style>
```

# 終わりに

cssも同じファイルに`<style>`で書けて1ファイルで済ませられるのがいいですね。