Chapter 04

Vuetify Home画面 デザインの話

hello_yogurt
hello_yogurt
2022.07.23に更新

App.vue編集

App.vue
<template>
  <v-app>
    <v-app-bar app>
        <v-toolbar-title
        class="text-caption text-sm-body-1"
        @click="$router.push('/')"
        style="cursor:pointer">
        Amazon Stock
        </v-toolbar-title>
        <v-spacer></v-spacer>

        <v-toolbar-title
        v-if="login  && !$vuetify.breakpoint.xs"
        class="text-body-1">
        username: {{GetUser()}}
        </v-toolbar-title>


        <v-btn
          depressed outlined
          v-if="login"
          class="mx-2"
          to="/mypage"
        >MYPAGE</v-btn>

        <v-dialog
        transition="dialog-bottom-transition"
        max-width="600"
        v-if="login"
      >

        <template v-slot:activator="{ on, attrs }">
          <v-btn
            depressed outlined
            v-bind="attrs"
            v-on="on"
            color="error"
            v-if="login"
            class="mx-2"

          >LOGOUT</v-btn>
        </template>
        <template v-slot:default="dialog">
          <v-card>
            <v-toolbar
            >LOGOUT</v-toolbar>
            <v-card-text>
              <v-row justify="center" align-content="center" class="my-10">
                  ログアウトしますか?
              </v-row>
            </v-card-text>
            <v-row justify="center" align-content="center" class="my-5">
                <v-btn
                  depressed outlined
                  color="error"
                  v-if="login"
                  class="mx-5"
                  v-on:click='Logout'
                >LOGOUT</v-btn>
            </v-row>



            <v-card-actions class="justify-end">
              <v-btn
                text
                @click="dialog.value = false"
              >Close</v-btn>
            </v-card-actions>
          </v-card>
        </template>
      </v-dialog>


        <v-dialog
        transition="dialog-bottom-transition"
        max-width="600"
        v-if="!loading"
      >

        <template v-slot:activator="{ on, attrs }">
          <v-btn
            depressed outlined
            v-bind="attrs"
            v-on="on"
            v-if="!login"
            class="mx-5"
          >LOGIN</v-btn>
        </template>
        <template v-slot:default="dialog">
          <v-card>
            <v-toolbar
            >LOGIN</v-toolbar>


            <v-form ref="form" v-model="valid" lazy-validation>
              <v-row justify="center" align-content="center" class="my-5">
                <v-col cols="8" sm="4" md="4" lg="4" xl="4">
                  <v-text-field
                    v-model="credentials.username"
                    :counter="70"
                    label="username"
                    maxlength="70"
                    required
                  />
                </v-col>
              </v-row>
              <v-row justify="center" align-content="center" class="my-10">
                <v-col cols="8" sm="4" md="4" lg="4" xl="4">
                  <v-text-field
                    type="password"
                    v-model="credentials.password"
                    :counter="20"
                    label="password"
                    maxlength="20"
                    required
                  />
                </v-col>
              </v-row>
              <v-row justify="center" align-content="center" class="ma-10">
                <v-col cols=4>
                  <v-btn
                    depressed
                    :disabled="!valid"
                    v-on:click='Login(); GetUser();'
                    color="primary"
                    class="ma-10">
                    ログイン
                  </v-btn>
                </v-col>
              </v-row>
            </v-form>
            <v-card-actions class="justify-end">
              <v-btn
                text
                @click="dialog.value = false"
              >Close</v-btn>
            </v-card-actions>
          </v-card>
        </template>
      </v-dialog>

        <v-dialog
        transition="dialog-bottom-transition"
        max-width="600"
        v-if="!loading"
      >
        <template v-slot:activator="{ on, attrs }">
          <v-btn
            v-bind="attrs"
            v-on="on"
            v-if="!login"
            class="mx-5"
          >SIGNUP</v-btn>
        </template>

        <template v-slot:default="dialog">
          <v-card>
            <v-toolbar
            >新規登録</v-toolbar>

            <v-layout
                row
                fill-height
                justify-center
                align-center
                v-if="loading"
            >
                <v-progress-circular
                :size="50"
                color="primary"
                indeterminate
                />
            </v-layout>

            <v-form v-else ref="form" v-model="valid" lazy-validation>
              <v-row justify="center" align-content="center" class="my-5">
                <v-col cols="8" sm="4" md="4" lg="4" xl="4">
                  <v-text-field
                    v-model="credentials.username"
                    :counter="70"
                    label="username"
                    maxlength="70"
                    required
                  />
                </v-col>
              </v-row>
              <v-row justify="center" align-content="center" class="my-10">
                <v-col cols="8" sm="4" md="4" lg="4" xl="4">
                  <v-text-field
                    type="password"
                    v-model="credentials.password"
                    :counter="20"
                    label="password"
                    maxlength="20"
                    required
                  />
                </v-col>
              </v-row>
              <v-row justify="center" align-content="center" class="my-10">
                <v-col cols=4>
                  <v-btn
                    depressed
                    :disabled="!valid"
                    v-on:click='CreateUser'
                    color="primary"
                    class="ma-10">
                    登録
                  </v-btn>
                </v-col>
              </v-row>
            </v-form>

            <v-card-actions class="justify-end">
              <v-btn
                text
                @click="dialog.value = false"
              >Close</v-btn>
            </v-card-actions>
          </v-card>
        </template>

      </v-dialog>

    </v-app-bar>
    <v-footer app>
     <v-row>
      <v-col cols=6 sm="12" md="12" lg="12" xl="12"
      class="text-caption text-sm-body-1"
      >
        Amazon Stock
      </v-col>
      <v-col cols=6
        v-if="login  && $vuetify.breakpoint.xs"
        class="text-caption px-0"
      >
        username: {{GetUser()}}
      </v-col>
     </v-row>
    </v-footer>
    <v-main class="pa-0">
      <router-view />
    </v-main>
  </v-app>
</template>

<script>
import axios from 'axios';
import Swal from 'sweetalert2';
import Cookies from 'js-cookie'


const csrfToken = Cookies.get('csrftoken')
const requestHeader = {
  headers: {
    'X-CSRFToken': csrfToken
  }
}


export default {
    name: 'App',
    data: () => ({
        info: [],
        user: [],
        credentials: {},
        valid:true,
        loading:false,
        login:false,
        rules: {
        username: [
            v => !!v || "ユーザー名は必須です",
            v => (v && v.length > 4) || "ユーザー名は5文字以上でなければなりません",
            v => /^[a-z0-9_]+$/.test(v) || "許可されていない文字が入力されています"
        ],
        password: [
            v => !!v || "パスワードは必須です",
            v => (v && v.length > 4) || "ユーザー名は5文字以上でなければなりません"
        ]
        }
    }),
    mounted() {
        this.checkLoggedIn();
        this.TokenGet();
    },
    methods: {
      Login() {
            if (this.$refs.form.validate()) {
            this.loading = true;
            axios.post("/auth/", this.credentials).then(res => {
                this.$session.start();
                this.$session.set('token', res.data.token);
                Cookies.set('username', this.credentials.username);
                const username = this.credentials.username;
                axios
                  .get("/users/"+username+"/")
                  .then(response => {
                    this.user = response;
                    console.log(this.user.data[0]);
                    Cookies.set('id', this.user.data[0].id);
                    console.log("取得成功", response);
                    this.$router.go({path: this.$router.currentRoute.path, force: true,})
                  });
            // eslint-disable-next-line
            }).catch(e => {
                this.loading = false;
                Swal.fire({
                type: 'warning',
                title: 'Error',
                text: 'ユーザー名もしくはパスワード、または両方が間違っています',
                showConfirmButton:false,
                showCloseButton:false,
                timer:3000
                })
            })
            }
        },
        GetUser(){
          const username = Cookies.get('username');
          return username;
        },
        CreateUser(){
          axios
            .post(
              "/create_user/",
              this.credentials,
              requestHeader
            )
           .then((res) => {
                 console.log(res);
                 this.posts = res.data.posts;
                 this.$router.go({path: this.$router.currentRoute.path, force: true})
            })
            .catch((err) => {
              console.log(err);
            });
        },
        checkLoggedIn() {
        this.$session.start();
        if (this.$session.has("token")) {
            this.login = true;

        }
      },
        TokenGet() {
        axios
          .get("/")
          .then(response => {
            this.info = response;
            console.log("取得成功", response);
          });
      },
        Logout() {
        this.$session.start();
        this.$session.remove("token");
        this.login = false;
        Cookies.remove('id');
        Cookies.remove('username');
        this.$router.go({path: this.$router.currentRoute.path, force: true})
      },
    }
}
</script>

frontend/src/main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import vuetify from "./plugins/vuetify";
import VueSession from 'vue-session';
import axios from 'axios'
import VueHead from 'vue-head'

Vue.use(VueSession)
Vue.use(VueHead)

Vue.config.productionTip = false;

axios.defaults.baseURL = "http://localhost:8000"
axios.defaults.withCredentials = true;


new Vue({
  router,
  vuetify,
  render: h => h(App)
}).$mount("#app");

frontend/src/components/HelloWorld.vue
<template>
  <v-main>
      <v-container class="" fluid>
        <v-row
          class="" style=""
          justify="center" align-content="center"
        >
        <v-col cols="12" class="border pa-0">
            <v-img
              height="100%"
              width="100%"
              src="@/assets/img/home_1.jpg"
              alt="home_1_shopping_basket"
            >
            <div class="d-flex flex-column justify-center align-center" style="height:100%">
              <h1 class="
              text-h5
              text-sm-h1
              my-5">Amazon Stock</h1>
              <h2 class="
              text-caption
              text-sm-h5
              " >Amazon商品の在庫を毎日
                <span style="color:green;">LINE</span>チェック</h2>
            </div>
            </v-img>
          </v-col>
        </v-row>
      </v-container>

      <v-container class="" fluid>
        <v-row>
          <v-col
            cols="12" sm="6" md="6" lg="6" xl="6"
            class="pa-0">
            <v-img
              src="@/assets/img/home_2.jpg"
              alt="home_2_search"
              height="100%">
            </v-img>
          </v-col>
          <v-col
            cols="12" sm="6" md="6" lg="6" xl="6"
            class="">
            <v-img
              src="@/assets/img/bubbles1.png"
              alt="bubbles1"
              width="50%">
            </v-img>
            <div class="d-flex flex-column justify-center ml-15">
              <h3 class="text-h4 my-10">商品ページのURLを登録するだけ</h3>
              <h4 class="text-h6 font-weight-light">Amazonの商品ページURLだけでスクレイピングできます</h4>
              <p class="ml-7 blue--text" style="font-size:600%;">
                <span style="
                font-weight:bold;
                background: linear-gradient(transparent 75%, yellow 75%);
                ">1</span>
                </p>
            </div>
          </v-col>
        </v-row>

      </v-container>

      <v-container class="" fluid>
        <v-row>
          <v-col
            cols="12" sm="6" md="6" lg="6" xl="6"
            class="pa-0 amber lighten-4">
            <v-img
              src="@/assets/img/bubbles2.png"
              alt="bubbles2"
              width="50%">
            </v-img>
            <div class="d-flex flex-column justify-center ml-15 pa-15">
              <p class="my-n10 ml-15 blue--text" style="font-size:600%;">
                <span style="
                font-weight:bold;
                background: linear-gradient(transparent 75%, yellow 75%);
                ">2</span>
                </p>
              <h3 class="text-h4 my-10 amber--text text--darken-4">アプリが在庫を毎日チェック</h3>
              <h4 class="text-h6 font-weight-light">AWSのCloudWatchでアラームをセットします</h4>
            </div>
          </v-col>
          <v-col
            cols="12" sm="6" md="6" lg="6" xl="6"
            class="pa-0">
            <v-img
              src="@/assets/img/home_3.jpg"
              alt="home_3_check"
              height="100%">
            </v-img>
          </v-col>
        </v-row>

      </v-container>

      <v-container class="" fluid>
        <v-row>
          <v-col
          cols="1"
          class="pa-0">
          <v-img
            src="@/assets/img/bubbles2.png"
            alt="bubbles2"
            width="100%">
          </v-img>
          </v-col>
          <v-col
            cols="12" sm="6" md="6" lg="6" xl="6"
            class="pa-0">
            <v-img
              src="@/assets/img/home_4.jpg"
              alt="home_4_line"
              width="120%"
              height="100%"
              >
            </v-img>
          </v-col>

          <v-col
            cols="12" sm="5" md="5" lg="5" xl="5"
            class="d-flex align-center justify-center">
            <div class="d-flex flex-column justify-center">
              <p class="ml-15 blue--text" style="font-size:600%;">
                <span style="
                font-weight:bold;
                background: linear-gradient(transparent 75%, yellow 75%);
                ">3</span>
                </p>
              <h3 class="text-h4 my-10">
                <span style="
                background: linear-gradient(transparent 75%, lime 75%);
                ">
                  LINE
                  </span>で通知を簡単に受け取れる</h3>
              <h4 class="text-h6 font-weight-light">
                <span class="green--text">
                  LINE API
                </span>
                を使って定時刻に通知します</h4>
            </div>
          </v-col>
        </v-row>

      </v-container>

      <v-container class="blue d-flex align-center justify-center" fluid style="height:600px">
        <div
          class="white d-flex flex-column justify-center align-center"
          style="height:80%; width:80%;"
        >
          <h2 class="">Amazon Stockを始める</h2>
          <h3 class="my-10">Amazon商品を登録する</h3>
          <v-btn
           to="/"
           class="my-5"
           x-large
           depressed
           tile
           elevation="2"
           color=""
           v-if="login">
            GET STARTED
          </v-btn>
          <h5 v-if="!login" class="px-5">新規登録(SIGNUP)、ログインが必須です。右上バーから新規登録(SIGNUP)、ログインをお願いします</h5>
          <h5 v-if="!login" class="px-5">ログインできましたら、ここに「GET STARTED」ボタンが表示されますので、そちらからお進み下さい</h5>
        </div>
      </v-container>

      <v-container class="px-10 orange" fluid style="height:600px">
        <v-row
          class="" style="height:100%"
          justify="center" align-content="center"
        >
            <div class="d-flex flex-column justify-center align-center" style="height:100%">
              <h1 class="my-10" style="font-size:300%; color:white;">Amazon Stock</h1>
              <h2 style="font-size:100%; color:white;">made by</h2>
              <h2 style="font-size:100%; color:white;">Tsuyoshi Takezawa</h2>
            </div>
        </v-row>
      </v-container>

  </v-main>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    login:false,
  }),
  mounted() {
      this.checkLoggedIn();
  },
  methods:{
    checkLoggedIn() {
      this.$session.start();
      if (this.$session.has("token")) {
          this.login = true;
      }
    },
  },
};
</script>


frontend/src/assets/imgに画像入れる
home_1.jpg ~ home_4.jpg

<v-img
   src="@/assets/img/home_3.jpg"
   alt="home_3_check"
   height="100%">
</v-img>

Vuetify デザインの話

公式(#Grid system)

https://vuetifyjs.com/ja/components/grids/

v-row
→列

<v-row justify="center" align-content="center">
よく使う

v-col
→行

<v-col col=number>
→numberで横幅をいい感じにしてくれる

cols="12" sm="6" md="6" lg="6" xl="6"
→smが自分のPC モバイルのxsはcolにないので、colsが直接効く

divにもclassでflexが使える

<div class="d-flex flex-column justify-center ml-15">

https://vuetifyjs.com/ja/styles/flex/

マージン、パディング

設定できる種類をご紹介します。数値は0から12までで設定できます。
* ma・・・marginのtop,bottom,left,rightの4方向
* mx・・・marginのleft,rightの2方向
* my・・・marginのtop,bottomの2方向
* mt・・・marginのtopの1方向
* mb・・・marginのbottomの1方向
* ml・・・marginのleftの1方向
* mr・・・marginのrightの1方向
パディングはmをpにする
マイナス(ネガティブ)はma-n3のようにnつける

https://masa-enjoy.com/vuetify-beginner#toc8

https://vuetifyjs.com/ja/styles/spacing/

テキストの文字大きさ

.text-{value} for xs
.text-{breakpoint}-{value} for sm, md, lg and xl
value プロパティは次のいずれかです:

h1
h2
h3
h4
h5
h6
subtitle-1
subtitle-2
body-1
body-2
button
caption
overline

例
class="text-caption text-sm-body-1"

テキスト 色

<h3 class="text-h4 my-10 amber--text text--darken-4">

https://qiita.com/senth/items/10e9980f21174cf599a1

モバイルやディスプレイの大きさ(breakpoint)で表示を変える

v-if="!$vuetify.breakpoint.xs"
→xsじゃない時に表示する