環境
- Windows 10 Pro 2004
- Oracle VM VirtualBox 6.1.12 (2020/07/14)
- Oracle VM VirtualBox 6.1.12 Extension Pack
- CentOS Linux 7 (2003) (2020/04/27)
- Git 2.24.2 (IUS版)
- Google Chrome
- Oracle Java SE Development Kit 11.0.7
- Apache Tomcat 9.0.37 (2020/07/05)
- Apache 2.4.6 (2013/07/22)
- nvm v0.35.3 (2020/03/06)
- node v12.18.3 (npm v6.14.6) (2020/07/22)
- Vuestic Admin 2.1.0 (2020/02/04)
- Visual Studio Code 1.47.2 (2020/06)
- Eclipse IDE for Enterprise Java Developers 2020-06 R (4.16.0)
- Spring Tool 4 (aka Spring Tool Suite 4) 4.7.0.RELEASE (2020/06/19)
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サイト
- EPICMAX
- EPICMAX -> VUESTIC (VIEW ON GITHUB)
1-2. ライセンス
1-3. インストール要件
- Node.js (>=8.9)
- npm version 3+ (or yarn version 1.16+) and Git
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タブで確認できる。

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}/*" } }
参考
- Vetur (Vue tooling for VS Code.)
- vue-devtools (Browser devtools extension for debugging Vue.js applications.)
- Vue.jsで開発するときはvue-devtoolsもね
- Debugging in VS Code (Chromeを起動してVS Code上でデバッグ実行)
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, ], })
参考
- Routing | Vue.js
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' }) }, },
参考
- The Simplest Store | Getting Started - Vuex
Using store state in a component simply involves returning the state within a computed property, because the store state is reactive. Triggering changes simply means committing mutations in component methods.
Vuexストアの状態を使うにはコンポーネントの算出プロパティで状態を返せばよい。
- Getting Vuex State into Vue Components | State - Vuex
So how do we display state inside the store in our Vue components? Since Vuex stores are reactive, the simplest way to "retrieve" state from it is simply returning some store state from within a computed property:
Vuexストアの状態を使うにはコンポーネントの算出プロパティで状態を返せばよい。
- Commit with Payload | Mutations - Vuex
You can pass an additional argument to store.commit, which is called the payload for the mutation:
In most cases, the payload should be an object so that it can contain multiple fields, and the recorded mutation will also be more descriptive:
Vuexストアの状態を更新する場合はミューテーションを使う。追加の引数を渡す場合はオブジェクトを渡す。
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 }, }, }, }
参考
- v-bind Shorthand | Template Syntax - Vue.js
Vueの属性は"v-bind:属性名"で記述するが"v-bind"を省略して":属性名"とすることができる。
- Prop Casing (camelCase vs kebab-case) | Props - Vue.js
HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. That means when you’re using in-DOM templates, camelCased prop names need to use their kebab-cased (hyphen-delimited) equivalents:
プロパティ名はJavaScript内ではキャメルケース(2つ目以降の単語の頭文字を大文字)で記述する。HTML内ではケバブケース(単語をハイフン区切り)で記述する。
4-4. ログインユーザー名を返すWebサービスをSpring Bootで作成する
EclipseでSpring Bootのプロジェクトを作成する。
File -> New > Other -> Spring Boot -> Spring Starter Project


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
参考
- Building a RESTful Web Service | Spring Boot
- Controller Method CORS Configuration | Spring Boot
You can also add the @CrossOrigin annotation at the controller class level as well, to enable CORS on all handler methods of this class.
@CrossOriginアノテーションは個々のメソッドではなくコントローラークラス全体に付与することもできる。
- Angularの開発サーバー(http://localhost:4200)からApache-Tomcat(Spring Boot, Java)(http://localhost)へのアクセス
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('ログイン失敗') } }) }, }, }
参考
- Mutations Must Be Synchronous | Mutations - Vuex
One important rule to remember is that mutation handler functions must be synchronous.
ミューテーションハンドラ関数は同期的でなければならない。
- Actions | Vuex
Instead of mutating the state, actions commit mutations.
アクションはミューテーションをコミットする。
Actions can contain arbitrary asynchronous operations.
アクションは任意の非同期処理を含むことができる。
- Using Axios to Consume APIs | Vue.js
Axiosという非同期通信のライブラリがある。
- axios
Promise based HTTP client for the browser and node.js
- Response Schema | axios
サーバーからの戻り値は決められたプロパティが定義されたオブジェクト型になっている。
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. 変更前後の確認
変更前

変更後

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. 変更後の確認
変更後

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>
画面を確認する。

参考
- export 'AddPlaceModal' was not found in '~/components/AddPlaceModal.vue'
Vueではコンポーネントにはdefault exportを使う。named exportは使えない。import時に中括弧を付けないのが正解。
OK: import AddPlaceModal from '~/components/AddPlaceModal.vue'
NG: import { AddPlaceModal } from '~/components/AddPlaceModal.vue'
- What is $t in Vue.js
this.$t('key')で言語ファイル(src/i18n/ja.json)から文字列を取得できる。
- Vue I18n is internationalization plugin for Vue.js
言語ファイル用のプラグイン
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>
画面を確認する。

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>
画面を確認する。

11. サーバーにデプロイする
11-1. ベースになるパスを相対パスで指定する
デフォルトではサーバールートからのパス解決が行われるのでどこに配置してもパス解決できるように相対パスにする。
vue.config.js module.exports = { publicPath: './', }
参考
- publicPath | Configuration Reference | Vue CLI
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