3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Webアプリ開発】買い物リストアプリ

Posted at

はじめに

勉強も兼ねて、今回は買い物メモのアプリを作成することにしました。
もともと忘れっぽい性格なので、よく何を買うんだっけ、、、?
スーパーに行ってから考えるタイプなので、自作アプリで乗り越えられたらベストかなと。
シンプルに見やすく、継続して利用できそうなアプリにしたいと思います。

要件定義

想定場面
買い物に行く際、アプリを使用し、購入タスクをつける。

機能
リストへの追記・修正・削除
カテゴリ分け(食品・日用品など、自分で追加・編集可能)
検索機能

技術選定
フロントエンド:vue.js
バックエンド:python、flask
開発環境:VsCode

学習内容(~開発環境作成)

:black_nib: git

cmd
git init #gitを始めるおまじない

:black_nib: .gitignoreファイルの作成

.gitignore
node_modules/ #node_modules フォルダを無視
*.log #ログファイルを無視
.env #環境設定ファイルを無視

:black_nib: flask
・Pythonで書かれたフレームワークのこと
・最小限のコア機能やプラグイン、拡張モジュールを搭載し、必要な機能のみを追加できる
・シンプルで使いやすく、初心者向けのフレームワーク→覚えることが少ない

:black_nib: Node.js
・javascriptのサーバー側実行環境

cmd
node -v #インストールされているかのチェック

:black_nib: vue.js インストール時の選択項目
・typescript:(よくわかりませんでした)
・JSX support:JSX(javascript XML)→javascript内にHTMLなど別言語を組み込むかどうか
・vue Router:SPA(single page app)にするかどうかで使用、ページ移動をするかのようにURLを書き換える機能がある
・Pinia:公式状態管理アプリ
・Vitest:(よくわかりませんでした)
・EsLint:誤った構文のチェックをしてくれる
・Prettier:javascriptの自動フォーマッターで、インデント修正などをしてくれる
→今回はEsLint、Prettierのみ採用:star:

:black_nib: npm
npm=Node Package Manager
pnpm=performance NPM(javascriptのパッケージ管理ツール)

cmd
npm sreate vue@latest #Vueプロジェクトの作成
npm install #パッケージのインストール
npm run dev #開発サーバーを起動

コードの中身

App.vue
<template>
  <div class="flex flex-col min-h-screen">
    
    <!-- ✅ ヘッダー -->
    <header class="bg-green-200 text-gray-800 p-4 flex justify-between items-center">
      <div class="flex items-center space-x-2">
        <span class="text-xl">🛒</span>
        <h1 class="text-xl font-bold">Shopping-ListApp</h1>
      </div>
      <div>{{ currentDate }}</div>
    </header>

    <!-- ✅ 本体は左右2カラム -->
    <div class="flex flex-1">
      
      <!-- 左サイドバー -->
      <aside class="w-48 bg-gray-800 text-white p-4 space-y-4">
        <button class="w-full bg-gray-700 hover:bg-gray-600 p-2 rounded">Settings</button>
        <button class="w-full bg-gray-700 hover:bg-gray-600 p-2 rounded">Search</button>
        <button class="w-full bg-gray-700 hover:bg-gray-600 p-2 rounded">Add Item</button>
      </aside>

      <!-- 右側:フォームとリスト -->
      <main class="flex-1 p-6 bg-white">
        <!-- フォーム -->
        <div class="mb-4">
          <input
            v-model="newItem.name"
            placeholder="商品名"
            class="border p-2 mr-2"
          />
          <input
            v-model.number="newItem.quantity"
            type="number"
            placeholder="数量"
            class="border p-2 mr-2 w-20"
          />
          <select v-model="newItem.category" class="border p-2 mr-2">
            <option value="Food">Food</option>
            <option value="Household">Household</option>
          </select>
          <button @click="addItem" class="bg-green-500 text-white px-4 py-2 rounded">
            追加
          </button>
        </div>

        <!-- リスト -->
        <h1 class="text-2xl font-bold mb-4">買い物リスト</h1>
        <ul>
          <li
            v-for="item in items"
            :key="item.id"
            class="mb-2 p-2 border rounded flex justify-between items-center"
          >
            <span>🛒 {{ item.name }}{{ item.quantity }}</span>
            <button
              @click="deleteItem(item.id)"
              class="bg-red-500 text-white px-2 py-1 rounded ml-4"
            >
              🗑 削除
            </button>
          </li>
        </ul>
      </main>
    </div>
  </div>
</template>

<script>
import api from './api'

export default {
  data() {
    return {
      items: [],
      newItem: {
        name: '',
        quantity: 1,
        category: 'Food'
      },
      currentDate: new Date().toLocaleDateString()
    }
  },
  async created() {
    const response = await api.getItems()
    this.items = response.data
  },
  methods: {
    async addItem() {
      if (!this.newItem.name.trim()) return
      const response = await api.addItem(this.newItem)
      this.items.push(response.data)
      this.newItem = { name: '', quantity: 1, category: 'Food' }
    },
    async deleteItem(id) {
      await api.deleteItem(id)
      this.items = this.items.filter(item => item.id !== id)
    }
  }
}
</script>

<style scoped>
/* くすみグリーンの調整やロゴ用 */
.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

api.js
// src/api.js
import axios from 'axios'

const apiClient = axios.create({
  baseURL: 'http://localhost:5000', // FlaskのサーバーURL
  headers: {
    'Content-Type': 'application/json'
  }
})

export default {
  getItems: () => apiClient.get('/items'),
  addItem: (item) => apiClient.post('/items', item),
  deleteItem: (id) => apiClient.delete(`/items/${id}`),
  // 他の関数はあとで追加
}

app.py
from flask import Flask, jsonify, request
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Vueと通信できるようにする!

# 仮のリストデータ
items = [
    {"id": 1, "name": "Apples", "quantity": 5, "category": "Food", "checked": False},
    {"id": 2, "name": "Laundry Detergent", "quantity": 1, "category": "Household", "checked": False},
]

@app.route("/items", methods=["GET"])
def get_items():
    return jsonify(items)

@app.route("/items", methods=["POST"])
def add_item():
    data = request.json
    new_item = {
        "id": len(items) + 1,
        "name": data["name"],
        "quantity": data["quantity"],
        "category": data["category"],
        "checked": False
    }
    items.append(new_item)
    return jsonify(new_item), 201

@app.route("/items/<int:item_id>", methods=["DELETE"])
def delete_item(item_id):
    global items
    items = [item for item in items if item["id"] != item_id]
    return jsonify({"message": "Item deleted"}), 200

if __name__ == "__main__":
    app.run(debug=True)

現在のWEBアプリ状況

image.png
flaskとの連携・リストへの追加削除は完了
image.png

今後改良すること
・ヘッダーをつけて、横にカートをおしてる女性を白抜きで表示
・日付横に時刻まで入れて今の時間だとタイムセールかも??みたいな風に
・くすみグリーンの色合いで表示をしたいが、まっしろなまま動かず

想定イメージ(chatGPT作成)

syopping-listapp.image.png

おわりに

以前、試しに作ってみたアプリで、なんとなくvueやflaskを利用していましたが、
なぜそのような書き方をするのか、どう修正をすればエラーが解消できるのか等、
今回はかなり細かく調べながら行っています。
今後はデザインをより想定イメージに近づけるように改良を進めていきます。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?