Vuestic Admin 2.1.0 (Vue.js admin template)で管理画面を作成する

2020年7月27日(月)

環境

GitHub repository

vuestic-admin (forked from epicmaxco/vuestic-admin) | feature/mockブランチ

開発環境のディレクトリ構成

/
+-home
|   +-mizuki
|       +-.nvm
|       |   +-versions
|       |       +-node
|       |           +-v12.18.3
|       |
|       +-.local
|       |   +-share
|       |       +-applications
|       |           |-_home_mizuki_opt_eclipse-jee-202006_.desktop
|       |
|       +-opt
|       |   |-eclipse-jee -> /home/mizuki/opt/eclipse-jee-202006
|       |   |
|       |   +-eclipse-jee-202006
|       |
|       +-workspace
|       |   +-eclipse
|       |   +-github
|       |       +-vuestic-admin-2.1.0
|       |
|       +-www
|           +-html
|               +-static
|                   |-index.html
|
+-media
|   +-sf_sharedfolder
|
+-opt
|   |-apache-tomcat -> /opt/apache-tomcat-9.0.37
|   |
|   +-apache-tomcat-9.0.37
|   |   +-bin
|   |   |   |-startup.sh
|   |   |   |-shutdown.sh
|   |   |   |-setenv.sh
|   |   |
|   |   +-conf
|   |       |-tomcat-users.xml
|   |
|   |-java -> /opt/oracle-jdk-11.0.7
|   |
|   +-oracle-jdk-11.0.7
|       +-bin
|           |-java
|
+-etc
    +-selinux
        |-config
          

テンプレート(Vuestic Admin 2.1.0)のディレクトリ構成

VUESTIC-ADMIN-2.1.0
+-src
    +-app
    |   |-main-mock.js (store,router,i18nの読み込み(このファイル自体はvue.config.jsで読み込まれる))
    |
    +-components
    |   +-admin
    |   |   +-app-navbar
    |   |   |   +-components
    |   |   |   |   +-dropdowns
    |   |   |   |   |   |-ProfileDropdown-mock.vue (プロフィールドロップダウン(ユーザー名の表示を含む))
    |   |   |   |   |   |-SettingsDropdown-mock.vue (設定ドロップダウン(歯車アイコンの表示を含む))
    |   |   |   |   |
    |   |   |   |   |-AppNavbarActions-mock.vue (上部ナビゲーションバー 操作コンポーネント群)
    |   |   |   |
    |   |   |   |-AppNavbar-mock.vue (上部ナビゲーションバー)
    |   |   |
    |   |   +-app-sidebar
    |   |   |   |-AppSidebar-mock.vue (左側サイドバー)
    |   |   |   |-NavigationRoutes-mock.js (左側サイドバー 表示項目)
    |   |   |
    |   |   +-app-topbar
    |   |       |-AppLayout-mock.vue (上部トップバー(左側サイドバーを上部に表示するためのもの))
    |   |
    |   +-auth
    |   |   +-login
    |   |   |   |-Login-mock.vue (ログイン画面)
    |   |   |
    |   |   |-AuthLayout-mock.vue (ログイン画面 レイアウト)
    |   |
    |   +-dashboard
    |   |   |-Dashboard-chart-move-mock.vue (ダッシュボード画面 7日間移動平均折れ線グラフ)
    |   |   |-Dashboard-chart-new-mock.vue (ダッシュボード画面 新規陽性者数縦棒グラフ)
    |   |   |-Dashboard-info-new-mock.vue (ダッシュボード画面 新規陽性者数)
    |   |   |-Dashboard-info-positive-mock.vue (ダッシュボード画面 陽性率)
    |   |   |-Dashboard-info-test-mock.vue (ダッシュボード画面 検査実施件数)
    |   |   |-Dashboard-mock.vue (ダッシュボード画面)
    |   |
    |   +-report
    |   |   |-Report-datatable-move-mock.vue (レポート画面 データテーブル 7日間移動平均)
    |   |   |-Report-datatable-new-mock.vue (レポート画面 データテーブル 新規陽性者数)
    |   |   |-Report-mock.vue (レポート画面)
    |   |
    |   +-user
    |       |-User-profile-edit-info-mock.vue (ユーザープロフィール画面 編集 ユーザー情報の編集)
    |       |-User-profile-edit-security-mock.vue (ユーザープロフィール画面 編集 ログインとセキュリティ)
    |       |-User-profile-menu-mock.vue (ユーザープロフィール画面 メニュー)
    |       |-User-profile-mock.vue (ユーザープロフィール画面)
    |       |-User-profile-photo-mock.vue (ユーザープロフィール画面 写真と名前)
    |
    +-i18n
    |   |-index-mock.js (使用する言語ファイルの指定)
    |   |-ja-mock.json (日本語の言語ファイル)
    |
    +-router
    |   |-index-mock.js (ルーターの定義)
    |
    +-services
    |   +-themes-config
    |   |   +-themes-config
    |   |       |-index.js (テーマ色の定義)
    |   |
    |   +-vuesitc-ui (テンプレートで用意しているコンポーネント群の読み込み?)
    |       |-components.js
    |       |-index.js
    |       |-styles.scss
    |
    +-store
    |   |-index-mock.js (データ保持)
    |
    |-vue.config.js (設定)
          

1. Vuestic Admin 2.1.0をインストールする

1-1. Webサイト

1-2. ライセンス

1-3. インストール要件

Node.jsを最新版に更新する。

$ cd /home/mizuki

$ nvm ls-remote
->     v12.18.2   (LTS: Erbium)
       v12.18.3   (Latest LTS: Erbium)

$ nvm install 12.18.3
Now using node v12.18.3 (npm v6.14.6)

$ nvm ls
v12.18.2
->     v12.18.3
default -> 12.18.2 (-> v12.18.2)
node -> stable (-> v12.18.3) (default)
stable -> 12.18 (-> v12.18.3) (default)

$ nvm alias default 12.18.3
default -> 12.18.3 (-> v12.18.3)

$ nvm use default
Now using node v12.18.3 (npm v6.14.6)

$ nvm uninstall 12.18.2
Uninstalled node v12.18.2
        

1-4. インストール

インストール手順

$ cd /home/mizuki/workspace/github

# clone the repo
$ git clone https://github.com/epicmaxco/vuestic-admin.git vuestic-admin-2.1.0

# go into app's directory and install dependencies:
$ cd vuestic-admin-2.1.0

$ npm install

# serve with hot reload at localhost:8080 by default.
$ npm run serve
DONE  Compiled successfully in 43181ms 13:50:07
App running at:
- Local:   http://localhost:8081/
- Network: http://10.0.2.15:8081/
        

2. Linux上で監視できるファイル数を増やす

2-1. VSCodeでVuestic Adminのフォルダを開いてからnpm run serveするとエラーが発生する

Error: ENOSPC: System limit for number of file watchers reached, watch '/home/mizuki/workspace/github/vuestic-admin-2.1.0/public'
    at FSWatcher.start (internal/fs/watchers.js:169:26)
    at Object.watch (fs.js:1422:11)
    at createFsWatchInstance (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:38:15)
    at setFsWatchListener (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:81:15)
    at FSWatcher.NodeFsHandler._watchWithNodeFs (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:233:14)
    at FSWatcher.NodeFsHandler._handleDir (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:429:19)
    at FSWatcher.<anonymous> (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:477:19)
    at FSWatcher.<anonymous> (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:482:16)
    at FSReqCallback.oncomplete (fs.js:169:5)
Emitted 'error' event on FSWatcher instance at:
    at FSWatcher._handleError (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/index.js:260:10)
    at createFsWatchInstance (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:40:5)
    at setFsWatchListener (/home/mizuki/workspace/github/vuestic-admin-2.1.0/node_modules/chokidar/lib/nodefs-handler.js:81:15)
    [... lines matching original stack trace ...]
    at FSReqCallback.oncomplete (fs.js:169:5) {
  errno: -28,
  syscall: 'watch',
  code: 'ENOSPC',
  path: '/home/mizuki/workspace/github/vuestic-admin-2.1.0/public',
  filename: '/home/mizuki/workspace/github/vuestic-admin-2.1.0/public'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! vuestic-admin@2.0.0 serve: `vue-cli-service serve`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the vuestic-admin@2.0.0 serve script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/mizuki/.npm/_logs/2020-07-27T12_31_16_625Z-debug.log
        

2-2. 解決策

You can get your current inotify file watch limit by executing:

$ cat /proc/sys/fs/inotify/max_user_watches
8192
        

If you like to make your limit permanent, use:

$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
fs.inotify.max_user_watches=524288

$ sudo sysctl -p
fs.inotify.max_user_watches = 524288
        

参考

3. 開発用ツールをインストールする

Chromeの拡張機能vue-devtoolsを導入してブラウザ上でデバッグができる。Chromeの拡張機能でピン止めして常にツールバーに表示する。デベロッパーツールのVueタブで確認できる。

vue-devtools screenshot

Chromの拡張機能Debugger for Chromeを導入してVS Code上でデバッグ実行できる。

プロジェクトの設定に追記を行う。

vue.config.js

module.exports = {
  configureWebpack: {
    devtool: 'source-map'
  }
}
        

npm run serveで開発サーバーを実行させてからデバッガーの設定を行う。設定後に左のrunペインでvue.js: chromeを選択して実行ボタンを押す。

run > open configurations > chrome

{
  "type": "chrome",
  "request": "launch",
  "name": "vuejs: chrome",
  "url": "http://localhost:8081",
  "webRoot": "${workspaceFolder}/src",
  "breakOnLoad": true,
  "sourceMapPathOverrides": {
  "webpack:///src/*": "${webRoot}/*"
  }
}
        

参考

4. ログイン画面を表示する

4-1. 初期表示をログイン画面にする

ルーターの設定を書き換えてパスがワイルドカード(*)のリダイレクト先をダッシュボード画面ではなくログイン画面にする。

src/router/index-mock.js

import Vue from 'vue'
import Router from 'vue-router'
import AuthLayout from '../components/auth/AuthLayout-mock'
import AppLayout from '../components/admin/AppLayout-mock'

Vue.use(Router)

const EmptyParentComponent = {
  template: '<router-view></router-view>',
}

const demoRoutes = [
  {
    path: '/auth',
    component: AuthLayout,
    children: [
      {
        name: 'login',
        path: 'login',
        component: () => import('../components/auth/login/Login-mock.vue'),
      },
      {
        path: '',
        redirect: { name: 'login' },
      },
    ],
  },
  {
    name: 'Admin',
    path: '/admin',
    component: AppLayout,
    children: [
      {
        name: 'dashboard',
        path: 'dashboard',
        component: () => import('../components/dashboard/Dashboard-mock.vue'),
      },
      {
        name: 'report',
        path: 'report',
        component: () => import('../components/report/Report-mock.vue'),
      },
      {
        name: 'user',
        path: 'user',
        component: () => import('../components/user/User-profile-mock.vue'),
      },
    ],
  },
  {
    path: '/404',
    component: EmptyParentComponent,
    children: [
      {
        name: 'not-found-large-text',
        path: '/pages/not-found-large-text',
        component: () => import('../components/pages/404-pages/VaPageNotFoundLargeText.vue'),
      },
    ],
  },
  {
    path: '*',
    redirect: { name: 'login' },
  },
]

export default new Router({
  mode: process.env.VUE_APP_ROUTER_MODE_HISTORY === 'true' ? 'history' : 'hash',
  routes: [
    ...demoRoutes,
  ],
})
        

参考

4-2. ログインユーザー情報を保持する

Vuexストアにログインユーザー情報を保持する。stateにloginUserNameを追加する。mutationsにsetLoginUserNameを追加する。

変更前

src/store/index-mock.js

const store = new Vuex.Store({
  state: {},
  mutations: {},
})
        

変更後

src/store/index-mock.js

const store = new Vuex.Store({
  state: {
    loginUserName: '',
  },
  mutations: {
    setLoginUserName (state, payload) {
      state.loginUserName = payload.loginUserName
    },
  },
})
        

ログイン成功時にVuexストアのログインユーザー情報を更新する。

追加するコード

this.$store.commit('setLoginUserName', {
  loginUserName: '山田太郎',
})
        

変更前

src/components/auth/login/Login-mock.vue

methods: {
  onsubmit () {
    this.emailErrors = this.email ? [] : ['Email is required']
    this.passwordErrors = this.password ? [] : ['Password is required']
    if (!this.formReady) {
      return
    }
    /* ここに追加する。 */
    this.$router.push({ name: 'dashboard' })
  },
},
        

変更後

src/components/auth/login/Login-mock.vue

methods: {
  onsubmit () {
    this.emailErrors = this.email ? [] : ['Email is required']
    this.passwordErrors = this.password ? [] : ['Password is required']
    if (!this.formReady) {
      return
    }
    this.$store.commit('setLoginUserName', {
      loginUserName: '山田太郎',
    })
    this.$router.push({ name: 'dashboard' })
  },
},
        

参考

4-3. ナビゲーションバーにログインユーザー名を表示する

ユーザー名を表示している場所

src/components/admin/app-navbar/components/AppNavbarActions-mock.vue

<template>
  <div class="app-navbar-actions">
    <profile-dropdown class="app-navbar-actions__item app-navbar-actions__item--profile">
      <span>{{userName}}</span>
    </profile-dropdown>
  </div>
</template>

export default {
  props: {
    userName: {
      type: String,
      default: '',
    },
  },
}
        

AppNavbarActions.vueにuserNameのプロパティを設定している場所

src/components/admin/app-navbar/AppNavbar-mock.vue

<template>
  <nav class="app-navbar" :style="navbarStyle">
    <div class="app-navbar__content row">
      <app-navbar-actions
        class="app-navbar__actions md5 lg4"
        :user-name="userName"
        :is-top-bar.sync="isTopBarProxy"
      />
    </div>
  </nav>
</template>

export default {
  data () {
    return {
      userName: 'Vasili S',
    }
  },
}
        

userNameをVuexストアの状態から取得して算出プロパティで設定する。dataオブジェクトからはuserNameプロパティを削除する。

src/components/admin/app-navbar/AppNavbar-mock.vue

export default {
  data () {
    return {
    }
  },
  computed: {
    userName: {
      get () {
        return this.$store.state.loginUserName
      },
    },
  },
}
        

参考

4-4. ログインユーザー名を返すWebサービスをSpring Bootで作成する

EclipseでSpring Bootのプロジェクトを作成する。

File -> New > Other -> Spring Boot -> Spring Starter Project
New Spring Starter Project screenshot
New Spring Starter Project Dependencies screenshot

Webサービスの戻り値となるクラスを作成する。

src/main/java/com.example.demo/VueServiceResult.java

package com.example.demo;

public class VueServiceResult {
    private String loginUserName = "";
    private boolean loginSucceeded = false;

    public String getLoginUserName() {
        return loginUserName;
    }
    public void setLoginUserName(String loginUserName) {
        this.loginUserName = loginUserName;
    }
    public boolean isLoginSucceeded() {
        return loginSucceeded;
    }
    public void setLoginSucceeded(boolean loginSucceeded) {
        this.loginSucceeded = loginSucceeded;
    }
}
        

Webサービスのコントローラーとなるクラスを作成する。

src/main/java/com.example.demo/VueServiceController.java

package com.example.demo;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin
@RestController
public class VueServiceController {
    @GetMapping("/login")
    public VueServiceResult login(
        @RequestParam(required=true) String loginId,
        @RequestParam(required=true) String loginPassword
    ) {
        VueServiceResult result = new VueServiceResult();
        if (loginPassword.equals("1234")) {
            result.setLoginUserName("山田リモート太郎");
            result.setLoginSucceeded(true);
        }
        else {
            result.setLoginSucceeded(false);
        }
        return result;
    }
}
        

WARファイルを作成する。

VueServiceプロジェクト -> pom.xmlを右クリック -> Run -> Maven install

WARファイルをTomcatにデプロイする。

VueServiceプロジェクト/target/VueService-0.0.1-SNAPSHOT.war

参考

4-5. ログインユーザー名をWebサービスから取得する

入力されたログインIDとログインパスワード、ログインの成功をVuexストアのstateに保持する。

src/store/index-mock.js

const store = new Vuex.Store({
  state: {
    loginId: '',
    loginPassword: '',
    loginSucceeded: false,
  },
  mutations: {
    setLoginId (state, payload) {
      state.loginId = payload.loginId
    },
    setLoginPassword (state, payload) {
      state.loginPassword = payload.loginPassword
    },
    setLoginSucceeded (state, payload) {
      state.loginSucceeded = payload.loginSucceeded
    },
  },
})
        

Vuexストアのstateに保持したログインIDとログインパスワードを使ってWebサービスからログインユーザー名を取得するアクションを記述する。

src/store/index-mock.js

const store = new Vuex.Store({
  actions: {
    getLoginUserName (context) {
      return axios
        .get('http://localhost:8080/VueService-0.0.1-SNAPSHOT/login', {
          params: {
            loginId: context.state.loginId,
            loginPassword: context.state.loginPassword,
          },
        })
        .then(response => {
          if (response.data.loginSucceeded) {
            context.commit('setLoginUserName', {
              loginUserName: response.data.loginUserName,
            })
          }
          context.commit('setLoginSucceeded', {
            loginSucceeded: response.data.loginSucceeded,
          })
        })
        .catch(error => {
          console.log(error)
          context.commit('setLoginSucceeded', {
            loginSucceeded: false,
          })
        })
    },
  },
})
        

注意

axios.get()はGETのパラメーター値がundefinedの場合はURLにパラメーターが付加されない。空文字列の場合は付加される。

ログインに成功したらダッシュボード画面を表示する。

src/components/auth/login/Login-mock.vue

export default {
  methods: {
    onsubmit () {
      this.$store.commit('setLoginId', {
        loginId: this.email,
      })
      this.$store.commit('setLoginPassword', {
        loginPassword: this.password,
      })

      this.$store.dispatch('getLoginUserName')
        .finally(() => {
          if (this.$store.state.loginSucceeded) {
            console.log('ログイン成功')
            this.$router.push({ name: 'dashboard' })
          } else {
            console.log('ログイン失敗')
          }
        })
    },
  },
}
        

参考

5. 上部ナビゲーションバーを設定アイコンとユーザー名だけにする

5-1. 左側のロゴのリンク先をダッシュボードに変更する

src/components/admin/app-navbar/AppNavbar-mock.vue

<router-link
  class="app-navbar__logo mr-3"
  to="dashboard"
>
        

5-2. 中央のメッセージとメールアドレス、リポジトリへのリンクを削除する

src/components/admin/app-navbar/AppNavbar-mock.vue

<div class="app-navbar__center lg5 md4">
  <span
    class="app-navbar__text"
    :style="{color: this.$themes.gray}"
  >
    {{$t('navbar.messageUs')}} 
    <a
      href="mailto:hello@epicmax.co"
      target="_blank"
      class="app-navbar__mailto-link"
      :style="{color: this.$themes.primary}"
    >
      hello@epicmax.co
    </a>
  </span>
  <va-button
    href="https://github.com/epicmaxco/vuestic-admin"
    color="#000000"
    class="app-navbar__button"
    icon="fa fa-github"
    target="_blank"
  >
    {{$t('navbar.repository')}}
  </va-button>
</div>
        

背景の図形がナビゲーションバーの下部にはみ出すので削除する。

src/components/admin/app-navbar/AppNavbar-mock.vue

<div
  class="app-navbar__shape"
  :style="shapeStyle"
></div>
        

5-3. 右側のアクションを設定アイコンとユーザー名だけにする

色、メール、通知、言語のアイコンを削除する。

src/components/admin/app-navbar/components/AppNavbarActions-mock.vue

<color-dropdown class="app-navbar-actions__item"/>
<message-dropdown class="app-navbar-actions__item"/>
<notification-dropdown class="app-navbar-actions__item"/>
<language-dropdown class="app-navbar-actions__item"/>

<script>
import LanguageDropdown from './dropdowns/LanguageDropdown'
import NotificationDropdown from './dropdowns/NotificationDropdown'
import MessageDropdown from './dropdowns/MessageDropdown'
import ColorDropdown from './dropdowns/ColorDropdown'

export default {
  components: {
    ColorDropdown,
    MessageDropdown,
    NotificationDropdown,
    LanguageDropdown,
  }
}
</script>
        

5-4. 変更前後の確認

変更前

Vuestic Admin 上部ナビゲーションバー 変更前

変更後

Vuestic Admin 上部ナビゲーションバー 変更後

6. 左側サイドバーをダッシュボードとレポートだけにする

6-1. サイドバーの項目をダッシュボードとレポートだけにする

src/components/admin/app-sidebar/NavigationRoutes-mock.js

export const navigationRoutes = {
  root: {
    name: '/',
    displayName: 'navigationRoutes.home',
  },
  routes: [
    {
      name: 'dashboard',
      displayName: 'menu.dashboard',
      meta: {
        iconClass: 'vuestic-iconset vuestic-iconset-dashboard',
      },
    },
    {
      name: 'report',
      displayName: 'menu.report',
      meta: {
        iconClass: 'vuestic-iconset vuestic-iconset-statistics',
      },
    },
  ],
}
        

6-2. ルーターの定義をログイン画面、ダッシュボードとレポートだけにする

src/router/index-mock.js

import Vue from 'vue'
import Router from 'vue-router'
import AuthLayout from '../components/auth/AuthLayout-mock'
import AppLayout from '../components/admin/AppLayout-mock'

Vue.use(Router)

const EmptyParentComponent = {
  template: '<router-view></router-view>',
}

const demoRoutes = [
  {
    path: '/auth',
    component: AuthLayout,
    children: [
      {
        name: 'login',
        path: 'login',
        component: () => import('../components/auth/login/Login-mock.vue'),
      },
      {
        path: '',
        redirect: { name: 'login' },
      },
    ],
  },
  {
    name: 'Admin',
    path: '/admin',
    component: AppLayout,
    children: [
      {
        name: 'dashboard',
        path: 'dashboard',
        component: () => import('../components/dashboard/Dashboard-mock.vue'),
      },
      {
        name: 'report',
        path: 'report',
        component: () => import('../components/report/Report-mock.vue'),
      },
      {
        name: 'user',
        path: 'user',
        component: () => import('../components/user/User-profile-mock.vue'),
      },
    ],
  },
  {
    path: '/404',
    component: EmptyParentComponent,
    children: [
      {
        name: 'not-found-large-text',
        path: '/pages/not-found-large-text',
        component: () => import('../components/pages/404-pages/VaPageNotFoundLargeText.vue'),
      },
    ],
  },
  {
    path: '*',
    redirect: { name: 'login' },
  },
]

export default new Router({
  mode: process.env.VUE_APP_ROUTER_MODE_HISTORY === 'true' ? 'history' : 'hash',
  routes: [
    ...demoRoutes,
  ],
})
        

7. 上部ナビゲーションバーのプロフィールドロップダウンと左側サイドバーの項目名を日本語にする

7-1. 英語の言語ファイルをコピーして日本語の言語ファイルを用意する

英語の言語ファイル

src/i18n/en.json
          

日本語の言語ファイル

src/i18n/ja-mock.json
          

7-2. 上部ナビゲーションバーのプロフィールドロップダウンと左側サイドバーの項目名の文言を日本語に置き換える

src/i18n/ja-mock.json

{
  "menu": {
    "dashboard": "ダッシュボード",
    "report": "レポート",
  },
  "user": {
    "logout": "ログアウト",
    "profile": "プロフィール"
  },
}
        

7-3. 日本語の言語ファイルを使用する

src/i18n/index-mock.js

import Vue from 'vue'

// add translations directly to the application
Vue.i18n.add('ja', require('./ja-mock.json'))

// set the start locale to use
Vue.i18n.set('ja')

// set fallback for non-translated strings
Vue.i18n.fallback('ja')
        

7-4. 変更後の確認

変更後

Vuestic Admin 言語ファイル 変更後

8. レポート画面

8-1. テンプレートのデータテーブルのコンポーネントをコピーしてテーブルコンポーネントを作成する

src/components/report/Report-datatable-new-mock.vue

<template>
  <va-card
    class="table-widget"
    :title="$t('tables.labelsNew')"
  >
    <va-data-table
      :fields="fields"
      :data="newCount"
      no-pagination
    >
    </va-data-table>
  </va-card>
</template>

<script>
export default {
  data () {
    return {
    }
  },
  computed: {
    fields () {
      return [{
        name: 'date',
        title: this.$t('tables.headings.date'),
      }, {
        name: 'count',
        title: this.$t('tables.headings.newCount'),
      }]
    },
    newCount: {
      get () {
        if (this.$store.state.summaryTerm === 'short') {
          return [
            {
              date: '20200810',
              count: 197,
            },
            {
              date: '20200809',
              count: 331,
            },
            {
              date: '20200808',
              count: 429,
            },
            {
              date: '20200807',
              count: 462,
            },
            {
              date: '20200806',
              count: 360,
            },
            {
              date: '20200805',
              count: 263,
            },
            {
              date: '20200804',
              count: 309,
            },
          ]
        } else {
          return [
            {
              date: '20200810',
              count: 197,
            },
            {
              date: '20200809',
              count: 331,
            },
            {
              date: '20200808',
              count: 429,
            },
            {
              date: '20200807',
              count: 462,
            },
            {
              date: '20200806',
              count: 360,
            },
            {
              date: '20200805',
              count: 263,
            },
            {
              date: '20200804',
              count: 309,
            },
            {
              date: '20200803',
              count: 258,
            },
            {
              date: '20200802',
              count: 292,
            },
            {
              date: '20200801',
              count: 472,
            },
            {
              date: '20200731',
              count: 463,
            },
            {
              date: '20200730',
              count: 367,
            },
            {
              date: '20200729',
              count: 250,
            },
            {
              date: '20200728',
              count: 266,
            },
          ]
        }
      },
    },
  },
  methods: {
  },
}
</script>

<style lang="scss">
.table-widget {
  .va-card__body {
    height: 500px;
  }
}
</style>
        

8-2. テーブルコンポーネントを配置してレポート画面を作成する

src/components/report/Report-mock.vue

<template>
  <div class="report">
    <div class="row">
      <div class="flex xs6">
        <report-data-table-new />
      </div>
      <div class="flex xs6">
        <report-data-table-move />
      </div>
    </div>
  </div>
</template>

<script>
import ReportDataTableNew from './Report-datatable-new-mock'
import ReportDataTableMove from './Report-datatable-move-mock'

export default {
  name: 'report',
  components: {
    ReportDataTableNew,
    ReportDataTableMove,
  },
  data () {
    return {
    }
  },
}
</script>

<style lang="scss">
</style>
        

画面を確認する。

レポート画面 screenshot

参考

9. ダッシュボード画面

9-1. テンプレートのチャートのコンポーネントをコピーしてチャートコンポーネントを作成する

src/components/dashboard/Dashboard-chart-new-mock.vue

<template>
  <div class="dashboard-chart-new">
    <va-card
      class="chart-widget"
      :title="$t('dashboard.charts.newCount')"
    >
      <va-chart :data="verticalBarChartData" type="vertical-bar"/>
    </va-card>
  </div>
</template>

<script>
import VaChart from '../statistics/charts/va-charts/VaChart'

export default {
  name: 'dashboard-chart-new',
  components: { VaChart },
  data () {
    return {
    }
  },
  computed: {
    verticalBarChartData: {
      get () {
        if (this.$store.state.summaryTerm === 'short') {
          return {
            labels: [
              '20200804',
              '20200805',
              '20200806',
              '20200807',
              '20200808',
              '20200809',
              '20200810',
            ],
            datasets: [
              {
                label: '新規陽性者数',
                backgroundColor: this.$themes.primary,
                borderColor: 'transparent',
                data: [
                  309,
                  263,
                  360,
                  462,
                  429,
                  331,
                  197,
                ],
              },
            ],
          }
        } else {
          return {
            labels: [
              '0728',
              '0729',
              '0730',
              '0731',
              '0801',
              '0802',
              '0803',
              '0804',
              '0805',
              '0806',
              '0807',
              '0808',
              '0809',
              '0810',
            ],
            datasets: [
              {
                label: '新規陽性者数',
                backgroundColor: this.$themes.primary,
                borderColor: 'transparent',
                data: [
                  266,
                  250,
                  367,
                  463,
                  472,
                  292,
                  258,
                  309,
                  263,
                  360,
                  462,
                  429,
                  331,
                  197,
                ],
              },
            ],
          }
        }
      },
    },
  },
}
</script>

<style lang="scss">
.chart-widget {
  .va-card__body {
    height: 500px;
  }
}
</style>
        

9-2. テンプレートの情報のコンポーネントをコピーして情報コンポーネントを作成する

src/components/dashboard/Dashboard-info-new-mock.vue

<template>
  <div class="dashboard-info-new">
    <va-card class="mb-4" :color="info.color">
      <p class="display-2 mb-0" style="color: white;">{{ info.value }}</p>
      <p>{{$t('dashboard.info.' + info.text)}}</p>
    </va-card>
  </div>
</template>

<script>
export default {
  name: 'dashboard-info-new',
  data () {
    return {
      info: {
        color: 'success',
        value: '197',
        text: 'newCount',
        icon: '',
      },
    }
  },
  methods: {
  },
}
</script>

<style lang="scss">
</style>
        

9-3. チャートコンポーネントと情報コンポーネントを配置してダッシュボード画面を作成する

src/components/dashboard/Dashboard-mock.vue

<template>
  <div class="dashboard">
    <div class="row">
      <div class="flex xs6">
        <dashboard-chart-new-mock />
      </div>
      <div class="flex xs6">
        <dashboard-chart-move-mock />
      </div>
    </div>
    <div class="row">
      <div class="flex xs3">
        <dashboard-info-new-mock />
      </div>
      <div class="flex xs3">
        <dashboard-info-test-mock />
      </div>
      <div class="flex xs3">
        <dashboard-info-positive-mock />
      </div>
    </div>
  </div>
</template>

<script>
import DashboardChartNewMock from './Dashboard-chart-new-mock'
import DashboardChartMoveMock from './Dashboard-chart-move-mock'
import DashboardInfoNewMock from './Dashboard-info-new-mock'
import DashboardInfoTestMock from './Dashboard-info-test-mock'
import DashboardInfoPositiveMock from './Dashboard-info-positive-mock'

export default {
  name: 'dashboard',
  components: {
    DashboardChartNewMock,
    DashboardChartMoveMock,
    DashboardInfoNewMock,
    DashboardInfoTestMock,
    DashboardInfoPositiveMock,
  },
  methods: {
  },
}
</script>

<style lang="scss">
</style>
        

画面を確認する。

ダッシュボード画面 screenshot

10. ユーザープロフィール画面

10-1. テンプレートのカードのコンポーネントをコピーして画像と名前を表示するコンポーネントを作成する

src/components/user/User-profile-photo-mock.vue

<template>
  <va-card
    image="https://picsum.photos/300/200/?image=1043"
    :title="$t('userProfile.userNameTitle')"
  >
    {{userName}}
  </va-card>
</template>

<script>
export default {
  data () {
    return {
    }
  },
  computed: {
    userName: {
      get () {
        return this.$store.state.loginUserName
      },
    },
  },
  methods: {
  },
}
</script>

<style lang="scss">
</style>
        

10-2. テンプレートのリストのコンポーネントをコピーしてメニューを表示するコンポーネントを作成する

src/components/user/User-profile-menu-mock.vue

<template>
  <va-list fit>
    <va-list-label>
      {{ $t('userProfile.menuTitle') }}
    </va-list-label>

    <va-item clickable @click="onInfoClick">
      <va-item-section>
        <va-item-label>
          {{ $t('userProfile.menuInfoLabel') }}
        </va-item-label>

        <va-item-label caption>
          {{ $t('userProfile.menuInfoCaption') }}
        </va-item-label>
      </va-item-section>
    </va-item>

    <va-item clickable @click="onSecurityClick">
      <va-item-section>
        <va-item-label>
          {{ $t('userProfile.menuSecurityLabel') }}
        </va-item-label>

        <va-item-label caption>
          {{ $t('userProfile.menuSecurityCaption') }}
        </va-item-label>
      </va-item-section>
    </va-item>
  </va-list>
</template>

<script>
export default {
  data () {
    return {
    }
  },
  computed: {
  },
  methods: {
    onInfoClick () {
      this.$emit('info')
    },
    onSecurityClick () {
      this.$emit('security')
    },
  },
}
</script>

<style lang="scss">
</style>
        

10-3. 画像と名前のコンポーネント、メニューのコンポーネントを配置してユーザープロフィール画面を作成する

src/components/user/User-profile-mock.vue

<template>
  <div class="user-profile">
    <div class="row">
      <div class="flex xs4">
        <user-profile-photo />
        <user-profile-menu
          class="mt-3"
          v-on:info="onMenuInfoClick"
          v-on:security="onMenuSecurityClick"
        /><!-- margin-top:3 -->
      </div>
      <div class="flex xs8">
        <user-profile-edit-info v-if="showInfo" />
        <user-profile-edit-security v-if="showSecurity" />
      </div>
    </div>
  </div>
</template>

<script>
import UserProfilePhoto from './User-profile-photo-mock'
import UserProfileMenu from './User-profile-menu-mock'
import UserProfileEditInfo from './User-profile-edit-info-mock'
import UserProfileEditSecurity from './User-profile-edit-security-mock'

export default {
  name: 'user-profile',
  components: {
    UserProfilePhoto,
    UserProfileMenu,
    UserProfileEditInfo,
    UserProfileEditSecurity,
  },
  props: {
  },
  data () {
    return {
    }
  },
  computed: {
    showInfo () {
      return this.$store.state.userProfileShowInfo
    },
    showSecurity () {
      return this.$store.state.userProfileShowSecurity
    },
  },
  methods: {
    onMenuInfoClick (event) {
      this.$store.commit('setUserProfileShowInfo', {
        show: true,
      })
      this.$store.commit('setUserProfileShowSecurity', {
        show: false,
      })
    },
    onMenuSecurityClick (event) {
      this.$store.commit('setUserProfileShowInfo', {
        show: false,
      })
      this.$store.commit('setUserProfileShowSecurity', {
        show: true,
      })
    },
  },
}
</script>

<style lang="scss">
</style>
        

画面を確認する。

ユーザープロフィール画面 screenshot

11. サーバーにデプロイする

11-1. ベースになるパスを相対パスで指定する

デフォルトではサーバールートからのパス解決が行われるのでどこに配置してもパス解決できるように相対パスにする。

vue.config.js

module.exports = {
  publicPath: './',
}
        

参考

11-2. PWA (Progressive Web Apps)の設定を削除する

PWAの機能は使わない。ブラウザで実行するとServiceWorkerのエラーが出るのでPWAの項目全体を削除する。

vue.confing.js

module.exports = {
  pwa: {
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      swSrc: './src/service-worker.js',
      importWorkboxFrom: 'local',
    },
  },
}
        

11-3. リリース用のファイルを作成する

$ npm run build
        

11-4. サーバーにアップロードする

distディレクトリ配下に出力されたファイルをサーバーにアップロードする。

$ ls -al
合計 264
drwxrwxr-x.  6 mizuki mizuki    213  8月 17 21:37 .
drwxrwxr-x. 13 mizuki mizuki   4096  8月 17 21:37 ..
drwxrwxr-x.  2 mizuki mizuki   4096  8月 17 21:37 css
-rw-rw-r--.  1 mizuki mizuki 120266  8月 17 21:37 favicon.ico
drwxrwxr-x.  2 mizuki mizuki   4096  8月 17 21:37 fonts
drwxrwxr-x.  3 mizuki mizuki  20480  8月 17 21:37 img
-rw-rw-r--.  1 mizuki mizuki   5700  8月 17 21:37 index.html
drwxrwxr-x.  2 mizuki mizuki   8192  8月 17 21:37 js
-rw-rw-r--.  1 mizuki mizuki    338  8月 17 21:37 manifest.json
-rw-rw-r--.  1 mizuki mizuki  61442  8月 17 21:37 precache-manifest.56bc693003be43b11be620b566524bb4.js
-rw-rw-r--.  1 mizuki mizuki     24  8月 17 21:37 robots.txt
-rw-rw-r--.  1 mizuki mizuki   1067  8月 17 21:37 service-worker.js
        

サンプルページを表示する