ウェルカムページは、全4回に渡って構築します。
1/4 ウェルカムページを構築するコンポーネントファイルの作成
4/4 レスポンシブデザインの対応(最終的なコード記載)(今ここ)
前回までのおさらい
前回は、スクロールによってツールバーの色を変化させる実装を行いました。
現在のウェルカムページはこのようになっています。
今回達成すること
今回は3つを実装し、ウェルカムページを完成させます。
- ツールバーに会員登録、ログインリンクを追加
- ツールバーメニューをレスポンシブデザインに対応
- フッターにコピーライトを追加
最終的にHerokuにpushして本番環境での動作も確認します。
途中でエラーにつまづいた方
このページ下部に最終的なコードを置いていますのでコピペしてください。
ツールバーに会員登録、ログインリンクを追加
ツールバーに会員登録、ログインリンクを追加します。
このリンクは、
- ウェルカムページと
- ログイン前のツールバー(まだ未作成)
で使用するのでコンポーネントとして切り分けます。
コンポーネントファイルを作成する
まずは「beforeLogin」ディレクトリに
root $ touch front/components/beforeLogin/{loginLink.vue,signupLink.vue}
それぞれにリンクボタンを作成します。
front/components/beforeLogin/loginLink.vue
<template>
<v-btn
text
class="ml-2 font-weight-bold"
color="black"
to="/login"
>
{{ $t('pages.login') }}
</v-btn>
</template>
<script>
export default {
}
</script>
front/components/beforeLogin/signupLink.vue
<template>
<v-btn
outlined
dark
color="myblue"
class="ml-2 font-weight-bold"
to="/signup"
>
{{ $t('pages.signup') }}
</v-btn>
</template>
<script>
export default {
}
</script>
Vuetifyにオリジナルカラーを追加する
会員登録のリンクカラーはcolor="myblue"
と設定しました。
これは現在のprimary
ではリンクが見えにくいためです。
vuetify
プロパティに新しい色myblue
を追加しましょう。
front/nuxt.config.js
vuetify: {
...
themes: {
light: {
primary: '4080BE',
info: '4FC1E9',
success: '44D69E',
warning: 'FEB65E',
error: 'FB8678',
background: 'f6f6f4',
// 追加
myblue: '1867C0'
}
}
}
},
nuxt-i18nを書き換える
nuxt-i18n
の翻訳ファイル
front/locales/ja.json
{
...
// pagesに書き換え
"pages": {
"signup": "会員登録",
"login": "ログイン"
}
}
ツールバーにコンポーネントを追加する
作成したリンクを
front/components/welcome/welAppBar.vue
...
</v-toolbar-items>
<!-- 2つのコンポーネントを追加 -->
<signup-link />
<login-link />
</v-app-bar>
</template>
...
コンテナを起動してブラウザで確認してみましょう。
root $ docker-compose up
きれいに表示されましたね。
リンク先ページを作成していないので、クリックしてもエラーになります。
レスポンシブデザインに対応する
レスポンシブに対応するため、ウィンドウ幅が768px未満の場合はツールバーメニューをポップアップ形式にします。
表示・非表示を切り替えるCSSクラスは、この記事で追加したhidden-ipad-and-up(down)
を使用します。
その他、アプリ名と「このサイトについて」も特定のブレイクポイントで非表示にするようCSSクラスを追加します。
front/components/welcome/welAppBar.vue
...
<!-- 1. class="hidden-mobile-and-down" 追加 -->
<v-toolbar-title
class="hidden-mobile-and-down"
>
{{ appName }}
</v-toolbar-title>
<v-spacer />
<!-- 2. hidden-ipad-and-down 追加 -->
<v-toolbar-items class="ml-2 hidden-ipad-and-down">
<!-- 3. :class="{ 'hidden-sm-and-down': (menu.title === 'about') }" 追加 -->
<v-btn
v-for="(menu, i) in menus"
:key="`menu-btn-${i}`"
text
:class="{ 'hidden-sm-and-down': (menu.title === 'about') }"
@click="goTo(menu.title)"
>
{{ $t(`menus.${menu.title}`) }}
</v-btn>
</v-toolbar-items>
<signup-link />
<login-link />
<!-- 4. 追加 -->
<v-menu
bottom
nudge-left="110"
nudge-width="100"
>
<template v-slot:activator="{ on }">
<v-app-bar-nav-icon
class="hidden-ipad-and-up"
v-on="on"
/>
</template>
<v-list
dense
class="hidden-ipad-and-up"
>
<v-list-item
v-for="(menu, i) in menus"
:key="`menu-list-${i}`"
exact
@click="goTo(menu.title)"
>
<v-list-item-title>
{{ $t(`menus.${menu.title}`) }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<!-- ここまで -->
</v-app-bar>
</template>
...
-
:class="{ 'hidden-sm-and-down': (menu.title === 'about') }"
...{ 'CSSクラス': Boolean }
=> オブジェクトの値がtrue
の場合にCSSクラスが有効になる書き方。
実装画面はこのようになります。
アプリタイトル
モバイル(426px)未満で非表示。
ツールバーメニュー
ipad(768px)未満で非表示。
ポップアップメニュー(v-menu)
ipad(768px)未満で表示。
メニュー「このサイトについて」
sm(960px)未満で非表示。
フッターにコピーライトを追加する
フッターにコピーライトを追加しましょう。
<!-- まるっと書き換え -->
<template>
<div :style="{ marginTop: `${height}px`}">
<v-footer
absolute
dark
color="black"
:height="height"
>
<v-col
cols="12"
class="py-0"
>
<div class="text-center text-body-2">
&copy;{{ copyRightYear }}
<strong>{{ appName }}</strong>
</div>
</v-col>
</v-footer>
</div>
</template>
<script>
export default {
data ({ $config: { appName } }) {
return {
appName,
height: 32
}
},
computed: {
copyRightYear () {
const beginningYear = 2020
const thisYear = new Date().getFullYear()
return (beginningYear < thisYear)
? `${beginningYear} - ${thisYear}` : beginningYear
}
}
}
</script>
-
:style="{ marginTop: ${height}px}"
...v-footer
にabsolute
を指定するとコンテンツが被さるため、同じ高さのマージントップを指定する。 -
copyRightYear ()
...beginningYear
より現在の年が大きければ2020 - 2021
と表示され、そうでなければ2020
と表示される。
これでフッターが完成です。
Herokuにpushする
これにてウェルカムページの完成です。Herokuにpushしましょう。
各メニューのコンテンツコードを追加する場合は、このページ下部をコピペしてください。
ブランチ名を分かりやすい名前に変更します。
root $ cd front
front $ git branch -m 20200803_welcome_page_layout
front $ git branch
* 20200803_welcome_page_layout
git branch -m <新しいブランチ名>
... 今いるブランチの名前を変更する。
masterブランチにマージします。
front $ git add -A
front $ git commit -m "finished_welcome_page"
front $ git checkout master
front $ git merge 20200803_welcome_page_layout # 自分のブランチ名に読み替えてください
GitHubとHerokuにpushします。
front $ git push
front $ git push heroku
Herokuに環境変数をセットします。
お好きな名前でどうぞ。
front $ heroku config:set APP_NAME=BizPlanner
front $ heroku config
APP_NAME: BizPlanner
Herokuを開いて本番環境の表示も確認しておきましょう。
front $ heroku open
本番環境の確認が取れたら「root」ディレクトリをGitHubにpushします。
front $ cd ..
root $ git commit -am "front_finished_welcome_page"
root $ git push
以上でウェルカムページの実装を終わります。
まとめ
今回はツールバーにリンクを設定し、レスポンシブに対応するようCSSクラスを追加しました。
これでどのデバイスにも対応できるようになりましたね。
このウェルカムページは全4回に渡って構築しました。
- 1/4 ウェルカムページを構成するコンポーネントファイル群を作成しました。
- 2/4 アイキャッチ画像・アプリ名・メニューボタンを表示しました。
- 3/4 スクロールを検知しツールバーの色を変化させました。
- 4/4 ウェルカムページのレスポンシブデザイン(今ここ)
いやーここまで来たあなた、本当にお疲れ様でした。
次回は?
次回から複数回に分けて、会員登録ページ・ログインページを構築します。
どうぞお楽しみに!
最終的なコード
ウェルカムページの最終的なコード。
エラーが発生した方はここのコードをコピペしてください。
環境変数
front/.env
APP_NAME=BizPnanner
nuxt.config.js
front/nuxt.config.js
export default {
mode: 'spa',
// Doc: https://ja.nuxtjs.org/blog/going-full-static/
target: 'server',
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
loading: { color: '#fff' },
css: [
'~/assets/sass/main.scss'
],
plugins: [
'plugins/axios'
],
// Doc: https://nuxtjs.org/api/configuration-components
components: true,
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module',
// Doc: https://www.npmjs.com/package/@nuxtjs/vuetify
'@nuxtjs/vuetify'
],
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// Doc: https://www.npmjs.com/package/nuxt-i18n
'nuxt-i18n'
],
// public ENV
// Doc: https://nuxtjs.org/guide/runtime-config/
publicRuntimeConfig: {
appName: process.env.APP_NAME // 追加
},
axios: {
},
vuetify: {
// 開発環境でcustomVariablesを有効にするフラグ
// Doc: https://vuetifyjs.com/ja/customization/a-la-carte/
treeShake: true,
customVariables: ['~/assets/sass/variables.scss'],
theme: {
themes: {
light: {
primary: '4080BE',
info: '4FC1E9',
success: '44D69E',
warning: 'FEB65E',
error: 'FB8678',
background: 'f6f6f4',
myblue: '1867C0'
}
}
}
},
// Doc: https://nuxt-community.github.io/nuxt-i18n/basic-usage.html#nuxt-link
i18n: {
// アプリがサポートしている言語
locales: ['ja', 'en'],
// デフォルトの言語
defaultLocale: 'ja',
// Doc: https://kazupon.github.io/vue-i18n/api/#properties
vueI18n: {
// defaultLocale(ja.json)にkeyがない場合に参照される(フォールバック)言語
fallbackLocale: 'ja',
// true => 翻訳に失敗した場合の警告メッセージを出力しない
// silentTranslationWarn: true,
// true => keyが全くない場合のみ警告が発生し、フォールバック時には警告は発生しない
silentFallbackWarn: true,
messages: {
ja: require('./locales/ja.json'),
en: require('./locales/en.json')
}
}
},
build: {
}
}
翻訳ファイル
front/locales/ja.json
{
"menus": {
"about": "サイトについて",
"products": "製品",
"price": "価格",
"contact": "お問合せ",
"company": "会社情報"
},
"pages": {
"signup": "会員登録",
"login": "ログイン"
}
}
レイアウトファイル
front/layouts/welcome.vue
<template>
<v-app>
<wel-app-bar
:menus="menus"
:img-height="imgHeight"
/>
<v-img
id="scroll-top"
dark
src="https://picsum.photos/id/20/1920/1080?blur=5"
gradient="to top right, rgba(19,84,122,.6), rgba(128,208,199,.9)"
:height="imgHeight"
>
<v-row
align="center"
justify="center"
:style="{ height: `${imgHeight}px` }"
>
<v-col
cols="12"
class="text-center"
>
<h1 class="display-1 mb-4">
未来を作ろう。ワクワクしよう。
</h1>
<h4
class="subheading"
:style="{ letterSpacing: '5px' }"
>
中小企業に特化した事業計画策定ツール
</h4>
</v-col>
</v-row>
</v-img>
<v-sheet>
<v-container
fluid
:style="{ maxWidth: '1280px' }"
>
<v-row
v-for="(menu, i) in menus"
:key="`menu-${i}`"
>
<v-col
:id="menu.title"
cols="12"
>
<v-card flat>
<v-card-title class="justify-center display-1">
{{ $t(`menus.${menu.title}`) }}
</v-card-title>
<v-card-text class="text-center">
{{ menu.subtitle }}
</v-card-text>
</v-card>
</v-col>
<v-col cols="12">
<div :is="`wel-${menu.title}`" />
</v-col>
</v-row>
</v-container>
</v-sheet>
<bef-login-footer />
</v-app>
</template>
<script>
import welAbout from '~/components/welcome/welAbout'
import welProducts from '~/components/welcome/welProducts'
import welPrice from '~/components/welcome/welPrice'
import welContact from '~/components/welcome/welContact'
import welCompany from '~/components/welcome/welCompany'
export default {
components: {
welAbout,
welProducts,
welPrice,
welContact,
welCompany
},
data () {
return {
imgHeight: 500, // 追加
menus: [
{ title: 'about', subtitle: 'このサイトはブログ"独学プログラマ"で公開されているチュートリアルのデモアプリケーションです' },
{ title: 'products', subtitle: '他にはない優れた機能の数々' },
{ title: 'price', subtitle: '会社の成長に合わせた3つのプラン' },
{ title: 'contact', subtitle: 'お気軽にご連絡を' },
{ title: 'company', subtitle: '私たちの会社' }
]
}
}
}
</script>
ツールバー
front/components/welcome/welAppBar.vue
<template>
<v-app-bar
app
:dark="!isScrollPoint"
:height="appBarHeight"
:color="toolbarStyle.color"
:elevation="toolbarStyle.elevation"
>
<app-logo
@click.native="goTo('scroll-top')"
/>
<v-toolbar-title
class="hidden-mobile-and-down"
>
{{ appName }}
</v-toolbar-title>
<v-spacer />
<v-toolbar-items class="ml-2 hidden-ipad-and-down">
<v-btn
v-for="(menu, i) in menus"
:key="`menu-btn-${i}`"
text
:class="{ 'hidden-sm-and-down': (menu.title === 'about') }"
@click="goTo(menu.title)"
>
{{ $t(`menus.${menu.title}`) }}
</v-btn>
</v-toolbar-items>
<signup-link />
<login-link />
<v-menu
bottom
nudge-left="110"
nudge-width="100"
>
<template v-slot:activator="{ on }">
<v-app-bar-nav-icon
class="hidden-ipad-and-up"
v-on="on"
/>
</template>
<v-list
dense
class="hidden-ipad-and-up"
>
<v-list-item
v-for="(menu, i) in menus"
:key="`menu-list-${i}`"
exact
@click="goTo(menu.title)"
>
<v-list-item-title>
{{ $t(`menus.${menu.title}`) }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
</template>
<script>
export default {
props: {
menus: {
type: Array,
default: () => []
},
imgHeight: {
type: Number,
default: 0
}
},
data ({ $config: { appName }, $store }) {
return {
appName,
scrollY: 0,
appBarHeight: $store.state.styles.beforeLogin.appBarHeight
}
},
computed: {
isScrollPoint () {
return this.scrollY > (this.imgHeight - this.appBarHeight)
},
toolbarStyle () {
const color = this.isScrollPoint ? 'white' : 'transparent'
const elevation = this.isScrollPoint ? 4 : 0
return { color, elevation }
}
},
mounted () {
window.addEventListener('scroll', this.onScroll)
},
beforeDestroy () {
window.removeEventListener('scroll', this.onScroll)
},
methods: {
onScroll () {
this.scrollY = window.scrollY
},
goTo (id) {
this.$vuetify.goTo(`#${id}`)
}
}
}
</script>
アプリロゴ
front/components/ui/appLogo.vue
<template>
<v-avatar
color="black"
size="34"
class="my-app-log"
>
<span class="white--text text-subtitle-2">
Biz
</span>
</v-avatar>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
.my-app-log {
margin-right: 8px;
cursor: pointer;
}
</style>
ログインリンク
front/components/beforeLogin/loginLink.vue
<template>
<v-btn
text
class="ml-2 font-weight-bold"
color="black"
to="/login"
>
{{ $t('pages.login') }}
</v-btn>
</template>
<script>
export default {
}
</script>
会員登録リンク
front/components/beforeLogin/signupLink.vue
<template>
<v-btn
outlined
dark
color="myblue"
class="ml-2 font-weight-bold"
to="/signup"
>
{{ $t('pages.signup') }}
</v-btn>
</template>
<script>
export default {
}
</script>
フッター
省略。上記に記載あり。
各メニューコンテンツのコード
各メニューのコンテンツはご自身の好きなように編集してください。
参考までに筆者のコードを載せておきます。
面倒な方はコピペして一部を自分用に編集してくださいね。
このサイトについて(welAbout.vue)
front/components/welcome/welAbout.vue
<template>
<div>
<v-card-title class="pb-8 text-subtitle-2 justify-center">
このアプリケーションの作り方は、下記URLに公開されています。
</v-card-title>
<v-card-title class="text-subtitle-2 justify-center">
「Rails6とNuxt.jsで作るユーザーJWT認証付きシングルページアプリケーション」
</v-card-title>
<v-card-text class="text-center">
<a
:href="blogUrl"
rel="nofollow"
target="_blank"
class="text-decoration-none"
>
{{ blogUrl }}
</a>
</v-card-text>
<v-card-title class="text-subtitle-2 justify-center">
採用している技術
</v-card-title>
<v-container
fluid
:style="{ maxWidth: '960px' }"
>
<v-row justify="space-around">
<div
v-for="(tec, i) in technologies"
:key="`tec-${i}`"
class="text-center pa-2"
>
<v-avatar
:color="tec.color"
size="80"
>
<span class="white--text">
{{ tec.name }}
</span>
</v-avatar>
<div class="pt-2 text-body-2 my-grey">
{{ tec.use }}
</div>
<div class="pt-2 text-body-2 my-grey">
{{ tec.v }}
</div>
</div>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
data () {
return {
blogUrl: 'https://blog.cloud-acct.com/categories/udemy',
technologies: [
{ name: 'Docker', v: 'v19.03+', use: '開発環境', color: '#2496ED' },
{ name: 'Rails api', v: 'v6.0+', use: 'サーバーサイド', color: '#CC0000' },
{ name: 'Postgre SQL', v: '', use: 'データベース', color: '#336791' },
{ name: 'Nuxt.js spa', v: 'v2.13+', use: 'フロントエンド', color: '#00C48D' },
{ name: 'Heroku', v: '', use: 'ホスティング', color: '#6762A6' },
{ name: 'Vuetify', v: 'v2.3+', use: 'CSSフレームワーク', color: '#1867C0' }
]
}
}
}
</script>
製品(welProducts.vue)
front/components/welcome/welProducts.vue
<template>
<v-row>
<v-col
cols="12"
sm="6"
>
<v-list flat>
<v-list-item
v-for="(point, i) in points"
:key="`point-${i}`"
>
<v-list-item-icon>
<v-icon
size="30"
:color="point.color"
v-text="point.icon"
/>
</v-list-item-icon>
<v-list-item-content>
<div
class="text-subtitle-1"
v-text="point.text"
/>
</v-list-item-content>
</v-list-item>
</v-list>
</v-col>
<v-col
cols="12"
sm="6"
>
<v-sparkline
:value="sparkline.value"
:gradient="sparkline.gradient"
:smooth="sparkline.radius || false"
:padding="sparkline.padding"
:line-width="sparkline.width"
:stroke-linecap="sparkline.lineCap"
:gradient-direction="sparkline.gradientDirection"
:fill="sparkline.fill"
:type="sparkline.type"
:auto-line-width="sparkline.autoLineWidth"
auto-draw
/>
</v-col>
</v-row>
</template>
<script>
export default {
data () {
const gradients = [
['#222'],
['#42b3f4'],
['red', 'orange', 'yellow'],
['purple', 'violet'],
['#00c6ff', '#F0F', '#FF0']
]
return {
points: [
{
icon: 'mdi-file-table-box-multiple-outline',
color: 'blue',
text: '直感的な操作で快適に事業計画書を作成'
},
{
icon: 'mdi-chart-bar',
color: 'green accent-4',
text: 'KPIから考えた夢物語ではない経営計画値を算出'
},
{
icon: 'mdi-chart-arc',
color: 'deep-orange',
text: 'ビジュアライズに優れたグラフツールで経営を可視化'
}
],
sparkline: {
width: 4,
radius: 10,
padding: 4,
lineCap: 'round',
gradient: gradients[4],
value: [0, 2, 5, 9, 5, 10, 8, 2, 9, 20],
gradientDirection: 'right',
gradients,
fill: false,
type: 'trend',
autoLineWidth: true
}
}
}
}
</script>
価格(welPrice.vue)
front/locales/ja.json
{
"menus": {
"about": "サイトについて",
"products": "製品",
"price": "価格",
"contact": "お問合せ",
"company": "会社情報",
// 追加
"payments": {
"month": "月払い",
"year": "年払い"
}
},
...
}
front/components/welcome/welPrice.vue
<template>
<v-row>
<v-col
cols="12"
class="py-0"
>
<v-card-actions class="py-0">
<v-spacer />
<v-radio-group
v-model="payment"
row
>
<v-radio
v-for="(pay, i) in payments"
:key="`pay-${i}`"
:label="$t(`menus.payments.${pay.label}`)"
:value="pay.label"
:color="pay.color"
/>
</v-radio-group>
</v-card-actions>
</v-col>
<v-col
v-for="(plan, i) in plans"
:key="`plan-${i}`"
cols="12"
:sm="12 / plans.length"
>
<v-card
max-width="402"
class="mx-auto"
>
<v-card-title
:class="['white--text', plan.color]"
>
{{ plan.name }}
</v-card-title>
<v-card-title class="justify-center">
{{ plan.exp }}
</v-card-title>
<v-divider />
<v-card-title class="justify-center">
メンバー
{{ plan.member }}
</v-card-title>
<v-divider />
<v-card-actions class="justify-center align-baseline">
月
<span class="px-2 display-1 font-weight-bold">
{{ yen(plan.price[payment]) }}
</span>
円
</v-card-actions>
<v-card-actions class="justify-center align-baseline">
年間 {{ yen(plan.price[payment] * 12) }} 円
</v-card-actions>
</v-card>
</v-col>
</v-row>
</template>
<script>
export default {
data () {
const payments = [
{ label: 'month', color: 'indigo' },
{ label: 'year', color: 'myblue' }
]
return {
payments,
payment: payments[1].label,
plans: [
{
name: 'Only',
color: 'info',
exp: '経営者1人のためのプラン',
member: '1人',
price: {
month: 1200,
year: 800
}
},
{
name: 'Small',
color: 'primary',
exp: '小規模事業に特化した1チーム専用',
member: '3人まで',
price: {
month: 2400,
year: 1800
}
},
{
name: 'Business',
color: 'indigo',
exp: '大規模なチームに戦略的経営を導入',
member: '10人まで',
price: {
month: 5000,
year: 4000
}
}
]
}
},
computed: {
yen () {
return (val) => {
return String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,')
}
}
}
}
</script>
お問合せ(welContact.vue)
front/components/welcome/welContact.vue
<template>
<v-row
justify="center"
>
<v-col
cols="12"
sm="10"
md="8"
>
<v-form
ref="contact"
v-model="isValid"
>
<v-container>
<v-row>
<v-col
cols="12"
sm="6"
>
<v-text-field
v-model="name"
:rules="nameRules"
:disabled="sentIt"
label="名前(必須)"
outlined
/>
</v-col>
<v-col
cols="12"
sm="6"
>
<v-text-field
v-model="email"
:rules="emailRules"
:disabled="sentIt"
label="メールアドレス(必須)"
outlined
validate-on-blur
/>
</v-col>
</v-row>
<v-textarea
v-model="contents"
:rules="contentRules"
:disabled="sentIt"
label="お問合せの内容をお聞かせください(必須)"
rows="5"
outlined
auto-grow
/>
<v-btn
:disabled="!isValid || loading || sentIt"
:loading="loading"
color="primary"
class="mr-2"
@click="onSend"
>
送信する
</v-btn>
<v-btn
text
@click="formReset"
>
キャンセル
</v-btn>
<div class="grey--text">
<small>実際には送信されません</small>
</div>
</v-container>
</v-form>
</v-col>
<v-snackbar
v-model="sentIt"
timeout="-1"
color="primary"
>
お問合せ内容が送信されました。メールアドレスへ担当者よりご連絡いたします。
<template v-slot:action="{ attrs }">
<v-btn
color="white"
text
v-bind="attrs"
@click="formReset"
>
Close
</v-btn>
</template>
</v-snackbar>
</v-row>
</template>
<script>
export default {
data () {
return {
isValid: false,
name: '',
nameRules: [
v => !!v || '名前を入力してください'
],
email: '',
emailRules: [
v => !!v || 'メールアドレスを入力してください',
v => /.+@.+\..+/.test(v) || 'メールアドレスが正しくありません'
],
contents: '',
contentRules: [
v => !!v || 'お問合せ内容を入力してください'
],
loading: false,
sentIt: false
}
},
methods: {
onSend () {
this.loading = true
setTimeout(() => {
this.loading = false
this.sentIt = true
}, 1500)
},
formReset () {
this.sentIt = false
this.$refs.contact.reset()
}
}
}
</script>
会社情報(welCompany.vue)
「assets」ディレクトリ直下に「images」ディレクトリを作成し、下記3つのイメージを保存します。
内容は何でもOKですが、名前だけは一致させてください。
front/assets/images
├── member1.png
├── member2.png
└── member3.png
front/components/welcome/welCompany.vue
<template>
<div>
<v-card-title class="font-weight-bold justify-center">
メンバー
</v-card-title>
<v-row justify="space-around">
<v-col
v-for="(member, i) in members"
:key="`member-${i}`"
>
<v-list-item>
<v-list-item-icon>
<img
:src="member.img"
:alt="member.nickname"
:aspect-ratio="192 / 336"
width="50"
>
</v-list-item-icon>
<v-list-item-content>
<div>
{{ member.name }}
</div>
<v-list-item-action-text>
{{ member.position }}
</v-list-item-action-text>
<v-list-item-action-text>
<v-btn
v-if="member.twitter"
:href="member.twitter"
target="_blank"
rel="noopener noreferrer"
small
icon
>
<v-icon size="18">
mdi-twitter
</v-icon>
</v-btn>
<v-btn
v-if="member.slack"
:href="member.slack"
target="_blank"
rel="noopener noreferrer"
small
icon
>
<v-icon size="18">
mdi-slack
</v-icon>
</v-btn>
</v-list-item-action-text>
</v-list-item-content>
</v-list-item>
</v-col>
</v-row>
<v-card-title class="font-weight-bold justify-center">
会社情報
</v-card-title>
<v-row justify="center">
<v-col
cols="12"
sm="10"
md="8"
>
<v-list
flat
dense
>
<v-divider />
<template v-for="(info, i) in infomations">
<v-list-item :key="`info-list-${i}`">
<v-list-item-icon>
<v-icon v-text="info.icon" />
</v-list-item-icon>
<v-list-item-content>
<a
v-if="info.link"
:href="info.link"
rel="nofollow"
target="_blank"
class="text-decoration-none"
>
{{ info.link }}
</a>
<div
v-else
class="text-subtitle-1"
v-text="info.text"
/>
</v-list-item-content>
</v-list-item>
<v-divider :key="`info-divider-${i}`" />
</template>
</v-list>
</v-col>
</v-row>
</div>
</template>
<script>
import member1 from '~/assets/images/member1.png'
import member2 from '~/assets/images/member2.png'
import member3 from '~/assets/images/member3.png'
export default {
data () {
const twitter = 'https://twitter.com/esegrammer'
const slack = 'https://join.slack.com/t/dokugaku-kai/shared_invite/zt-a5j1suoh-Y0fspHbo1fb0Wj6YTpDdXA'
const companyUrl = 'http://blog.cloud-acct.com'
return {
members: [
{ name: 'あんどう', position: '代表', img: member1, twitter, slack },
{ name: 'アローン', position: 'エンジニア', img: member2 },
{ name: 'カール', position: 'エンジニア', img: member3 }
],
infomations: [
{ icon: 'mdi-domain', text: 'BizPlanner株式会社' },
{ icon: 'mdi-link-variant', link: companyUrl },
{ icon: 'mdi-flag', text: '2020年7月に設立' },
{ icon: 'mdi-account-multiple', text: '3人のメンバー' },
{ icon: 'mdi-map-marker', text: '東京都港区虎ノ門一丁目17番1号' },
{ icon: 'mdi-handshake', text: 'Webアプリ開発・経営コンサルティング' }
]
}
}
}
</script>