Web 開発再入門 #20 ― Vue.js、Vuetify、axios

# Web 開発再入門 #20 ― Vue.js、Vuetify、axios

fmockup

## ★ 本ページは工事中 ★

## はじめに

Web アプリケーションのクライアント・サイド（ブラウザー・サイド）を開発します。

直観的で学習コストが低いといわれる Vue.js を使用します。
レスポンシブな画面を実現するために Vuetify を使用します。
Ajax を実現するために、axios を使用します。axios を使用することで、HTML 画面から Web サーバーの Web API を呼び出すことができます。

レスポンシブ画面：Web アプリケーションにおいて、ユーザーが使用するデバイスに応じて、自動的に画面の表示を最適化する画面のこと。
Ajax：Asynchronous JavaScript + XML

## ディレクトリ・ファイル構成

D:\\
└ Development\\
&nbsp;&nbsp;&nbsp;&nbsp;└ Workspace\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ fmockup\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ build\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ sql\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ src\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ vue-vite\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ node_modules\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ pages\\ ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ commons\\ ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;│&nbsp;├ header_for_login.vue ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;│&nbsp;├ header.vue ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;│&nbsp;└ token.vue ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ dialogs\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ ~~login_expired.html ← コレ~~
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ ~~login_expired.js ← コレ~~
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ ~~login_expired.vue ← コレ~~
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ login.html ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ login.js ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;└ login.vue ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ public\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;├ texts\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;│&nbsp;├ text_en.json ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;│&nbsp;├ text_ja.json ← コレ
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;│&nbsp;└ text_zh-CN.json ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;│&nbsp;└ favicon.ico ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ package.json ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ vite.config.js ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ WinSW.NET-nnn\\

## ファイルの文字コード

UTF-8、Unix 改行（LF）でよいように思います。

## Vue Vite プロジェクトの作成

1. コマンド・プロントで Vue-vite プロジェクトを作成する。
    ```bat:コマンド・プロンプト
    C:\Users\xxxx> D:
    D:\> cd D:\Development\Workspace\fmockup
    D:\Development\Workspace\fmockup> npm create vue

    Project name: ... vue-vite ← フォルダー名にもなる
    Add TypeScript? ... No
    Add JSX Support? ... No
    Add Vue Router for Single Page Application development? ... No / Yes ← No
    Add Pinia for state management? ... No / Yes ← No
    Add Vitest for Unit Testing? ... No
    Add an End-to-End Testing Solution? » No
    Add ESLint for code quality? ... Yes
    Add Prettier for code formatting? ... No

    D:\Development\Workspace\fmockup> cd vue-vite
    D:\Development\Workspace\fmockup\vue-vite> npm add vuetify
    D:\Development\Workspace\fmockup\vue-vite> npm install @mdi/font
    D:\Development\Workspace\fmockup\vue-vite> npm install axios

    D:\Development\Workspace\fmockup\vue-vite> exit
    ```

## フォルダーの作成

1. エクスプローラーで、フォルダー “D:\Development\Workspace\fmockup\vue-vite\pages”、“D:\Development\Workspace\fmockup\vue-vite\pages\commons” を作成する。

## ファイルの作成

1. テキスト・エディターで、ファイル “header_for_login.vue”、“header.vue”、“token.vue” を作成する。
各画面の共通画面コンポーネントとなります。
    ```vue:header_for_login.vue
    ```
    ```vue:header.vue
    ```
    ```vue:token.vue
    ```

2. テキスト・エディターで、ファイル “login.html”、“login.js”、“login.vue” を作成する。
ログイン画面になります。
    ```html:login.html
    <!DOCTYPE html>
    <html lang="en">
    	<head>
    		<meta charset="UTF-8">
    		<link rel="icon" href="/favicon.ico">
    		<meta name="viewport" content="width=device-width, initial-scale=1.0">
    		<title>fmockup</title>
    	</head>
    	<body>
    		<div id="app"></div>
    		<script type="module" src="./login.js"></script>
    	</body>
    </html>
    ```
    ```js:login.js
    import { createApp } from 'vue'
    import App from './login.vue'
    
    import 'vuetify/styles'
    import { createVuetify } from 'vuetify'
    import * as components from 'vuetify/components'
    import * as directives from 'vuetify/directives'
    import { aliases, mdi } from 'vuetify/iconsets/mdi'
    import '@mdi/font/css/materialdesignicons.css'
    
    const vuetify = createVuetify({
      components,
      directives,
      icons: {
        defaultSet: 'mdi',
        aliases,
        sets: {
          mdi,
        }
      },
    })
    
    const app = createApp(App)
    app.use(vuetify)
    app.mount('#app')
    ```
    ```vue:login.vue
    <template>
    	<v-app>
    		<common_header_for_login :logoutButton="logoutButton" @header_event="receive_header" />
    		<div v-if="display == ''">
    			<v-main>
    			</v-main>
    		</div>
    		<div v-if="display == 'error'">
    			<v-main>
    				<v-container fluid>
    					<v-row no-gutters>
    						<v-col><div style="color: red;">{{ message }}</div></v-col>
    					</v-row>
    				</v-container>
    			</v-main>
    		</div>
    		<div v-if="display == 'normal'">
    			<v-main>
    				<v-container fluid>
    					<v-row no-gutters>
    						<v-col><h3 class="display-1">{{ text_body_title }}</h3></v-col>
    					</v-row>
    					<v-row no-gutters>
    						<v-col><div style="color: red;">{{ message }}</div></v-col>
    					</v-row>
    					<v-row no-gutters>
    						<v-col>
    							<v-form>
    								<v-container fluid>
    									<v-row no-gutters>
    										<v-col :cols="field_md">
    											<div style="font-size: small; color: red;">{{ messages.userName }}</div>
    											<v-text-field autocomplete="off" prepend-icon="mdi-account" clearable :label="text_form_userId" v-model="userName" />
    										</v-col>
    									</v-row>
    									<v-row no-gutters>
    										<v-col :cols="field_md">
    											<div style="font-size: small; color: red;">{{ messages.password }}</div>
    											<v-text-field
    												autocomplete="off" prepend-icon="mdi-lock" clearable :label="text_form_password" v-model="password" 
    												v-bind:type="showPassword ? 'text' : 'password'"
    												v-bind:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
    												@click:append="showPassword = ! showPassword"
    											/>
    										</v-col>
    									</v-row>
    									<v-row no-gutters>
    										<v-col cols="auto">
    											<v-btn variant="outlined" color="success" @click="login()" :loading="loading" style="text-transform: none">{{ text_form_login }}</v-btn>
    										</v-col>
    									</v-row>
    								</v-container>
    							</v-form>
    						</v-col>
    					</v-row>
    				</v-container>
    			</v-main>
    		</div>
    	</v-app>
    	<common_texts @texts_event="receive_texts" />
    </template>
    
    <script setup>
    	import axios from 'axios'
    	import { ref } from 'vue'
    
    	const message = ref('')
    	const messages = ref({
    		'userName': '',
    		'password': '',
    	})
    
    	const field_md = ref(0)
    	const field_lg = ref(0)
    	const width = window.innerWidth
    	if (width < 600) {
    		field_md.value = 12
    		field_lg.value = 12
    	} else if (width < 1024) {
    		field_md.value = 6
    		field_lg.value = 10
    	} else {
    		field_md.value = 4
    		field_lg.value = 8
    	}
    
    	const loading = ref(true)
    	const display = ref('')
    	const logoutButton = ref(false)
    
    	const text_body_title = ref('')
    	const text_form_userId = ref('')
    	const text_form_password = ref('')
    	const text_form_login = ref('')
    	import common_texts from './commons/texts.vue'
    	function receive_texts(texts_status, texts_message, texts_value) {
    		if (texts_status == 'action-ok') {
    			const texts_json = texts_value
    			text_body_title.value = texts_json.login_body_title
    			text_form_userId.value = texts_json.login_form_userId
    			text_form_password.value = texts_json.login_form_password
    			text_form_login.value = texts_json.login_form_login
    		} else {
    			display.value = 'error'
    			message.value = texts_message
    		}
    	}
    
    	import common_header_for_login from './commons/header_for_login.vue'
    	function receive_header(header_status, header_message) {
    		if (header_status != 'action-ok') {
    			display.value = 'error'
    			message.value = header_message
    		}
    	}
    
    	const showPassword = ref(false)
    	const userName = ref('')
    	const password = ref('')
    
    	const formData = new FormData()
    	axios.post('/session/unauth', formData)
    	.then (function(response) {
    		;
    	})
    	.catch (function(error) {
    		message.value = 'Network trouble has occurred.'
    	})
    
    	async function login() {
    		loading.value = true
    		const formData = new FormData()
    		formData.append('userName', userName.value != null ? userName.value : '')
    		formData.append('password', password.value != null ? password.value : '')
    		await axios.post('/session/auth', formData)
    		.then (function(response) {
    			if (response.data.status == 'action-ok-expired') {
    				window.location.href = '/pages/login_expired.html'
    				return
    			} else if (response.data.status == 'action-ok') {
    				window.location.href = '/pages/home.html'
    				return
    			} else {
    				message.value = response.data.message
    				messages.value = response.data.messageMap
    			}
    		})
    		.catch (function(error) {
    			message.value = 'Network trouble has occurred.'
    		})
    		loading.value = false
    		return
    	}
    
    	loading.value = false
    	if (display.value == '') {
    		display.value = 'normal'
    	}
    </script>
    ```

~~3. テキスト・エディターで、ファイル “login_expired.html”、“login_expired.js”、“login_expired.vue” を作成する。
初回にログインした後の画面、または、パスワード期限切れの画面になります。~~
    ```html:login_expired.html
    ```
    ```js:login_expired.js
    ```
    ```vue:login_expired.vue
    ```

4. テキスト・エディターで、ファイル “text_en.json”、“text_ja.json”、“text_zh-CN.json” を作成する。
    ```json:text_en.json
    {
    	"comment_1": "--",
    	"comment_2": "-- fmockup HTML Strings.",
    	"comment_3": "--",
    	"comment_4": "--   Do not write comma at last line.",
    	"comment_5": "--",
    
    	"common_header_title": "fmockup",
    	"common_header_form_logout": "Logout",
    
    	"common_footer_company": "Xxxx Technologies Co., Ltd.",
    
    	"common_menu_home": "Home",
    	"common_menu_xxxx": "ＸＸＸＸ",
    	"common_menu_yyyy": "ＹＹＹＹ",
    	"common_menu_zzzz": "ＺＺＺＺ",
    	"common_menu_user_role": "User / Role",
    
    	"about_tab_home": "Home",
    	"about_tab_password": "Password",
    	"about_tab_about": "About",
    	"about_notice": "Do not display information in here that threatens security. for example: version information.",
    
    	"home_tab_home": "Home",
    	"home_tab_password": "Password",
    	"home_tab_about": "About",
    	"home_welcome": "Welcome to the fmockup.",
    
    	"login_body_title": "Login",
    	"login_form_userId": "User ID",
    	"login_form_password": "Password",
    	"login_form_login": "Login",
    
    	"login_expired_body_title": "Change Password",
    	"login_expired_form_currentPassword": "Current Password",
    	"login_expired_form_newPassword": "New Password",
    	"login_expired_form_confirmPassword": "Confirm Password",
    	"login_expired_form_save": "Save",
    	"login_expired_form_cancel": "Cancel",
    
    	"password_tab_home": "Home",
    	"password_tab_password": "Password",
    	"password_tab_about": "About",
    	"password_form_currentPassword": "Current Password",
    	"password_form_newPassword": "New Password",
    	"password_form_confirmPassword": "Confirm Password",
    	"password_form_save": "Save",
    	"password_form_cancel": "Cancel",
    	"password_notice_change": "If change your user's password, you have to use New Password in next Login.",	
    
    	"role_dialog_title_add": "Add Role",
    	"role_dialog_title_modify": "Modify Role",
    	"role_dialog_title_drop": "Drop Role",
    	"role_dialog_form_roleName": "Role Name",
    	"role_dialog_form_remark": "Remark",
    	"role_dialog_form_add": "Add",
    	"role_dialog_form_modify": "Modify",
    	"role_dialog_form_drop": "Drop",
    	"role_dialog_form_cancel": "Cancel",
    	"role_dialog_notice_sure": "Are you sure?",
    
    	"role_tran_dialog_title_assign": "Assign Transaction",
    	"role_tran_dialog_title_unassign": "Unassign Transaction",
    	"role_tran_dialog_form_roleName": "Role Name",
    	"role_tran_dialog_form_assign": "Assign",
    	"role_tran_dialog_form_unassign": "Unassign",
    	"role_tran_dialog_form_cancel": "Cancel",
    	"role_tran_dialog_notice_sure": "Are you sure?",
    
    	"role_list_tab_user_list": "User List",
    	"role_list_tab_role_list": "Role List",
    
    	"user_dialog_title_add": "Add User",
    	"user_dialog_title_modify": "Modify User",
    	"user_dialog_title_drop": "Drop User",
    	"user_dialog_form_userId": "User ID",
    	"user_dialog_form_newPassword": "New Password",
    	"user_dialog_form_confirmPassword": "Confirm Password",
    	"user_dialog_form_remark": "Remark",
    	"user_dialog_form_add": "Add",
    	"user_dialog_form_modify": "Modify",
    	"user_dialog_form_drop": "Drop",
    	"user_dialog_form_cancel": "Cancel",
    	"user_dialog_notice_change": "If change its user's password, its User Account will be locked.",
    	"user_dialog_notice_sure": "Are you sure?",
    
    	"user_role_dialog_title_assign": "Assign Role",
    	"user_role_dialog_title_unassign": "Unassign Role",
    	"user_role_dialog_form_userId": "User ID",
    	"user_role_dialog_form_assign": "Assign",
    	"user_role_dialog_form_unassign": "Unassign",
    	"user_role_dialog_form_cancel": "Cancel",
    	"user_role_dialog_notice_sure": "Are you sure?",
    
    	"user_list_tab_user_list": "User List",
    	"user_list_tab_role_list": "Role List",
    
    	"xxxx_dialog_title_add": "Add Xxxx",
    	"xxxx_dialog_title_modify": "Modify Xxxx",
    	"xxxx_dialog_title_drop": "Drop Xxxx",
    	"xxxx_dialog_form_name": "Xxxx Name",
    	"xxxx_dialog_form_flag": "Xxxx Flag",
    	"xxxx_dialog_form_value": "Xxxx Value",
    	"xxxx_dialog_form_remark": "Remark",
    	"xxxx_dialog_form_add": "Add",
    	"xxxx_dialog_form_modify": "Modify",
    	"xxxx_dialog_form_drop": "Drop",
    	"xxxx_dialog_form_cancel": "Cancel",
    	"xxxx_dialog_notice_sure": "Are you sure?",
    
    	"xxxx_list_tab_xxxx_list": "Xxxx List",
    
    	"yyyy_dialog_title_add": "Add Yyyy",
    	"yyyy_dialog_title_modify": "Modify Yyyy",
    	"yyyy_dialog_title_drop": "Drop Yyyy",
    	"yyyy_dialog_form_name": "Yyyy Name",
    	"yyyy_dialog_form_flag": "Yyyy Flag",
    	"yyyy_dialog_form_value": "Yyyy Value",
    	"yyyy_dialog_form_remark": "Remark",
    	"yyyy_dialog_form_add": "Add",
    	"yyyy_dialog_form_modify": "Modify",
    	"yyyy_dialog_form_drop": "Drop",
    	"yyyy_dialog_form_cancel": "Cancel",
    	"yyyy_dialog_notice_sure": "Are you sure?",
    
    	"yyyy_list_tab_yyyy_list": "Yyyy List",
    
    	"zzzz_dialog_title_add": "Add Zzzz",
    	"zzzz_dialog_title_modify": "Modify Zzzz",
    	"zzzz_dialog_title_drop": "Drop Zzzz",
    	"zzzz_dialog_form_name": "Zzzz Name",
    	"zzzz_dialog_form_flag": "Zzzz Flag",
    	"zzzz_dialog_form_value": "Zzzz Value",
    	"zzzz_dialog_form_remark": "Remark",
    	"zzzz_dialog_form_add": "Add",
    	"zzzz_dialog_form_modify": "Modify",
    	"zzzz_dialog_form_drop": "Drop",
    	"zzzz_dialog_form_cancel": "Cancel",
    	"zzzz_dialog_notice_sure": "Are you sure?",
    
    	"zzzz_list_tab_zzzz_list": "Zzzz List",
    
    	"comment_9": "最后行"
    }
    ```
    ```json:text_ja.json
    {
    	"comment_1": "--",
    	"comment_2": "-- fmockup HTML Strings.",
    	"comment_3": "--",
    	"comment_4": "--   最終行にコンマを付けてはならない。",
    	"comment_5": "--",
    
    	"common_header_title": "fmockup",
    	"common_header_form_logout": "ログアウト",
    
    	"common_footer_company": "ＸＸＸＸテクノロジーズ株式会社",
    
    	"common_menu_home": "ホーム",
    	"common_menu_xxxx": "ＸＸＸＸ",
    	"common_menu_yyyy": "ＹＹＹＹ",
    	"common_menu_zzzz": "ＺＺＺＺ",
    	"common_menu_user_role": "ユーザー・ロール",
    
    	"about_tab_home": "ホーム",
    	"about_tab_password": "パスワード",
    	"about_tab_about": "アバウト",
    	"about_notice": "ここにセキュリティを脅かす情報を表示してはならない。例えばバージョン情報。",
    
    	"home_tab_home": "ホーム",
    	"home_tab_password": "パスワード",
    	"home_tab_about": "アバウト",
    	"home_welcome": "fmockup へようこそ。",
    
    	"login_body_title": "ログイン",
    	"login_form_userId": "ユーザー ID",
    	"login_form_password": "パスワード",
    	"login_form_login": "ログイン",
    
    	"login_expired_body_title": "パスワード変更",
    	"login_expired_form_currentPassword": "現パスワード",
    	"login_expired_form_newPassword": "新パスワード",
    	"login_expired_form_confirmPassword": "パスワード確認",
    	"login_expired_form_save": "保存",
    	"login_expired_form_cancel": "キャンセル",
    
    	"password_tab_home": "ホーム",
    	"password_tab_password": "パスワード",
    	"password_tab_about": "アバウト",
    	"password_form_currentPassword": "現パスワード",
    	"password_form_newPassword": "新パスワード",
    	"password_form_confirmPassword": "パスワード確認",
    	"password_form_save": "保存",
    	"password_form_cancel": "キャンセル",
    	"password_notice_change": "パスワードを変更すると、次回のログインから新しいパスワードを使用する必要があります。",	
    
    	"role_dialog_title_add": "ロール追加",
    	"role_dialog_title_modify": "ロール更新",
    	"role_dialog_title_drop": "ロール削除",
    	"role_dialog_form_roleName": "ロール名",
    	"role_dialog_form_remark": "コメント",
    	"role_dialog_form_add": "追加",
    	"role_dialog_form_modify": "更新",
    	"role_dialog_form_drop": "削除",
    	"role_dialog_form_cancel": "キャンセル",
    	"role_dialog_notice_sure": "よろしいですか？",
    
    	"role_tran_dialog_title_assign": "トランザクション割当て",
    	"role_tran_dialog_title_unassign": "トランザクション解除",
    	"role_tran_dialog_form_roleName": "ロール名",
    	"role_tran_dialog_form_assign": "割当て",
    	"role_tran_dialog_form_unassign": "解除",
    	"role_tran_dialog_form_cancel": "キャンセル",
    	"role_tran_dialog_notice_sure": "よろしいですか？",
    
    	"role_list_tab_user_list": "ユーザーリスト",
    	"role_list_tab_role_list": "ロールリスト",
    
    	"user_dialog_title_add": "ユーザー追加",
    	"user_dialog_title_modify": "ユーザー更新",
    	"user_dialog_title_drop": "ユーザー削除",
    	"user_dialog_form_userId": "ユーザー ID",
    	"user_dialog_form_newPassword": "新パスワード",
    	"user_dialog_form_confirmPassword": "パスワード確認",
    	"user_dialog_form_remark": "コメント",
    	"user_dialog_form_add": "追加",
    	"user_dialog_form_modify": "更新",
    	"user_dialog_form_drop": "削除",
    	"user_dialog_form_cancel": "キャンセル",
    	"user_dialog_notice_change": "ユーザーのパスワードを変更すると、そのユーザーのアカウントはロックされます。",
    	"user_dialog_notice_sure": "よろしいですか？",
    
    	"user_role_dialog_title_assign": "ロール割当て",
    	"user_role_dialog_title_unassign": "ロール解除",
    	"user_role_dialog_form_userId": "ユーザー ID",
    	"user_role_dialog_form_assign": "割当て",
    	"user_role_dialog_form_unassign": "解除",
    	"user_role_dialog_form_cancel": "取消",
    	"user_role_dialog_notice_sure": "よろしいですか？",
    
    	"user_list_tab_user_list": "ユーザーリスト",
    	"user_list_tab_role_list": "ロールリスト",
    
    	"xxxx_dialog_title_add": "ＸＸＸＸ追加",
    	"xxxx_dialog_title_modify": "ＸＸＸＸ更新",
    	"xxxx_dialog_title_drop": "ＸＸＸＸ削除",
    	"xxxx_dialog_form_name": "ＸＸＸＸ名",
    	"xxxx_dialog_form_flag": "ＸＸＸＸフラグ",
    	"xxxx_dialog_form_value": "ＸＸＸＸ値",
    	"xxxx_dialog_form_remark": "コメント",
    	"xxxx_dialog_form_add": "追加",
    	"xxxx_dialog_form_modify": "更新",
    	"xxxx_dialog_form_drop": "削除",
    	"xxxx_dialog_form_cancel": "キャンセル",
    	"xxxx_dialog_notice_sure": "よろしいですか？",
    
    	"xxxx_list_tab_xxxx_list": "ＸＸＸＸリスト",
    
    	"yyyy_dialog_title_add": "ＹＹＹＹ追加",
    	"yyyy_dialog_title_modify": "ＹＹＹＹ更新",
    	"yyyy_dialog_title_drop": "ＹＹＹＹ削除",
    	"yyyy_dialog_form_name": "ＹＹＹＹ名",
    	"yyyy_dialog_form_flag": "ＹＹＹＹフラグ",
    	"yyyy_dialog_form_value": "ＹＹＹＹ値",
    	"yyyy_dialog_form_remark": "コメント",
    	"yyyy_dialog_form_add": "追加",
    	"yyyy_dialog_form_modify": "更新",
    	"yyyy_dialog_form_drop": "削除",
    	"yyyy_dialog_form_cancel": "キャンセル",
    	"yyyy_dialog_notice_sure": "よろしいですか？",
    
    	"yyyy_list_tab_yyyy_list": "ＹＹＹＹリスト",
    
    	"zzzz_dialog_title_add": "ＺＺＺＺ追加",
    	"zzzz_dialog_title_modify": "ＺＺＺＺ更新",
    	"zzzz_dialog_title_drop": "ＺＺＺＺ削除",
    	"zzzz_dialog_form_name": "ＺＺＺＺ名",
    	"zzzz_dialog_form_flag": "ＺＺＺＺフラグ",
    	"zzzz_dialog_form_value": "ＺＺＺＺ値",
    	"zzzz_dialog_form_remark": "コメント",
    	"zzzz_dialog_form_add": "追加",
    	"zzzz_dialog_form_modify": "更新",
    	"zzzz_dialog_form_drop": "削除",
    	"zzzz_dialog_form_cancel": "キャンセル",
    	"zzzz_dialog_notice_sure": "よろしいですか？",
    
    	"zzzz_list_tab_zzzz_list": "ＺＺＺＺリスト",
    
    	"comment_9": "最后行"
    }
    ```
    ```json:text_zh-CN.json
    {
    	"comment_1": "--",
    	"comment_2": "-- fmockup HTML Strings.",
    	"comment_3": "--",
    	"comment_4": "--   不允许在最后行里填写顿号。",
    	"comment_5": "--",
    
    	"common_header_title": "fmockup",
    	"common_header_form_logout": "注销",
    
    	"common_footer_company": "ＸＸＸＸ技术有限公司",
    
    	"common_menu_home": "主页",
    	"common_menu_xxxx": "ＸＸＸＸ",
    	"common_menu_yyyy": "ＹＹＹＹ",
    	"common_menu_zzzz": "ＺＺＺＺ",
    	"common_menu_user_role": "用户・角色",
    
    	"about_tab_home": "主页",
    	"about_tab_password": "密码",
    	"about_tab_about": "关于",
    	"about_notice": "在这里不允许显示出信息安全风险的信息。比如版本信息。",
    
    	"home_tab_home": "主页",
    	"home_tab_password": "密码",
    	"home_tab_about": "关于",
    	"home_welcome": "欢迎来到 fmockup。",
    
    	"login_body_title": "登录",
    	"login_form_userId": "用户 ID",
    	"login_form_password": "密码",
    	"login_form_login": "登录",
    
    	"login_expired_body_title": "更改密码",
    	"login_expired_form_currentPassword": "现密码",
    	"login_expired_form_newPassword": "新密码",
    	"login_expired_form_confirmPassword": "密码确认",
    	"login_expired_form_save": "保存",
    	"login_expired_form_cancel": "取消",
    
    	"password_tab_home": "主页",
    	"password_tab_password": "密码",
    	"password_tab_about": "关于",
    	"password_form_currentPassword": "现密码",
    	"password_form_newPassword": "新密码",
    	"password_form_confirmPassword": "密码确认",
    	"password_form_save": "保存",
    	"password_form_cancel": "取消",
    	"password_notice_change": "如果更新了密码，下次就在登录时需要用新密码了。",	
    
    	"role_dialog_title_add": "角色追加",
    	"role_dialog_title_modify": "角色更新",
    	"role_dialog_title_drop": "角色删除",
    	"role_dialog_form_roleName": "角色名",
    	"role_dialog_form_remark": "注释",
    	"role_dialog_form_add": "追加",
    	"role_dialog_form_modify": "更新",
    	"role_dialog_form_drop": "删除",
    	"role_dialog_form_cancel": "取消",
    	"role_dialog_notice_sure": "确定了吗？",
    
    	"role_tran_dialog_title_assign": "绑定事务",
    	"role_tran_dialog_title_unassign": "解开事务",
    	"role_tran_dialog_form_roleName": "事务名",
    	"role_tran_dialog_form_assign": "绑定",
    	"role_tran_dialog_form_unassign": "解开",
    	"role_tran_dialog_form_cancel": "取消",
    	"role_tran_dialog_notice_sure": "确定了吗？",
    
    	"role_list_tab_user_list": "用户清单",
    	"role_list_tab_role_list": "角色清单",
    
    	"user_dialog_title_add": "用户追加",
    	"user_dialog_title_modify": "用户更新",
    	"user_dialog_title_drop": "用户删除",
    	"user_dialog_form_userId": "用户 ID",
    	"user_dialog_form_newPassword": "新密码",
    	"user_dialog_form_confirmPassword": "密码确认",
    	"user_dialog_form_remark": "注释",
    	"user_dialog_form_add": "追加",
    	"user_dialog_form_modify": "更新",
    	"user_dialog_form_drop": "删除",
    	"user_dialog_form_cancel": "取消",
    	"user_dialog_notice_change": "如果更新用户密码，这账号就被锁定。",
    	"user_dialog_notice_sure": "确定了吗？",
    
    	"user_role_dialog_title_assign": "绑定角色",
    	"user_role_dialog_title_unassign": "解开角色",
    	"user_role_dialog_form_userId": "用户 ID",
    	"user_role_dialog_form_assign": "绑定",
    	"user_role_dialog_form_unassign": "解开",
    	"user_role_dialog_form_cancel": "取消",
    	"user_role_dialog_notice_sure": "确定了吗？",
    
    	"user_list_tab_user_list": "用户清单",
    	"user_list_tab_role_list": "角色清单",
    
    	"xxxx_dialog_title_add": "ＸＸＸＸ追加",
    	"xxxx_dialog_title_modify": "ＸＸＸＸ更新",
    	"xxxx_dialog_title_drop": "ＸＸＸＸ删除",
    	"xxxx_dialog_form_name": "ＸＸＸＸ名",
    	"xxxx_dialog_form_flag": "ＸＸＸＸ旗标",
    	"xxxx_dialog_form_value": "ＸＸＸＸ值",
    	"xxxx_dialog_form_remark": "注释",
    	"xxxx_dialog_form_add": "追加",
    	"xxxx_dialog_form_modify": "更新",
    	"xxxx_dialog_form_drop": "删除",
    	"xxxx_dialog_form_cancel": "取消",
    	"xxxx_dialog_notice_sure": "确定了吗？",
    
    	"xxxx_list_tab_xxxx_list": "ＸＸＸＸ清单",
    
    	"yyyy_dialog_title_add": "ＹＹＹＹ追加",
    	"yyyy_dialog_title_modify": "ＹＹＹＹ更新",
    	"yyyy_dialog_title_drop": "ＹＹＹＹ删除",
    	"yyyy_dialog_form_name": "ＹＹＹＹ名",
    	"yyyy_dialog_form_flag": "ＹＹＹＹ旗标",
    	"yyyy_dialog_form_value": "ＹＹＹＹ值",
    	"yyyy_dialog_form_remark": "注释",
    	"yyyy_dialog_form_add": "追加",
    	"yyyy_dialog_form_modify": "更新",
    	"yyyy_dialog_form_drop": "删除",
    	"yyyy_dialog_form_cancel": "取消",
    	"yyyy_dialog_notice_sure": "确定了吗？",
    
    	"yyyy_list_tab_yyyy_list": "ＹＹＹＹ清单",
    
    	"zzzz_dialog_title_add": "ＺＺＺＺ追加",
    	"zzzz_dialog_title_modify": "ＺＺＺＺ更新",
    	"zzzz_dialog_title_drop": "ＺＺＺＺ删除",
    	"zzzz_dialog_form_name": "ＺＺＺＺ名",
    	"zzzz_dialog_form_flag": "ＺＺＺＺ旗标",
    	"zzzz_dialog_form_value": "ＺＺＺＺ值",
    	"zzzz_dialog_form_remark": "注释",
    	"zzzz_dialog_form_add": "追加",
    	"zzzz_dialog_form_modify": "更新",
    	"zzzz_dialog_form_drop": "删除",
    	"zzzz_dialog_form_cancel": "取消",
    	"zzzz_dialog_notice_sure": "确定了吗？",
    
    	"zzzz_list_tab_zzzz_list": "ＺＺＺＺ清单",
    
    	"comment_9": "最后行"
    }
    ```

## ファイルの編集

1. テキスト・エディターで、ファイル “package.json”、“vite.config.js” を編集する。
    ```json:package.json
    {
      "name": "vue-vite",
      "version": "0.0.0",
      "private": true,
      "scripts": {
        "dev": "vite",
        "build": "vite build --outDir=../src/main/resources/static", ← ココ
        "preview": "vite preview",
        "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
      },
      "dependencies": {
        "axios": "^1.5.1",
        "vue": "^3.3.4",
        "vuetify": "^3.3.21"
      },
      "devDependencies": {
        "@mdi/font": "^7.3.67",
        "@vitejs/plugin-vue": "^4.3.4",
        "eslint": "^8.49.0",
        "eslint-plugin-vue": "^9.17.0",
        "vite": "^4.4.9"
      }
    }
    ```
    ```js:vite.config.js
    import { resolve } from 'path';
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    export default defineConfig({
    	plugins: [
    		vue(),
    	],
    	build: {
    		rollupOptions: {
    			input: {
    				about: resolve(__dirname, './pages/about.html'),
    				home: resolve(__dirname, './pages/home.html'),
    				login: resolve(__dirname, './pages/login.html'),
    				login_expired: resolve(__dirname, './pages/login_expired.html'),
    				password: resolve(__dirname, './pages/password.html'),
    				role_list: resolve(__dirname, './pages/role_list.html'),
    				user_list: resolve(__dirname, './pages/user_list.html'),
    				xxxx_list: resolve(__dirname, './pages/xxxx_list.html'),
    				yyyy_list: resolve(__dirname, './pages/yyyy_list.html'),
    				zzzz_list: resolve(__dirname, './pages/zzzz_list.html'),
    			}
    		}
    	},
    })
    ```

## Vue-Vite プロジェクトのビルド

1. コマンド・プロントで、Vue-vite プロジェクトをビルドする。
    ```bat:コマンド・プロンプト
    C:\Users\xxxx> D:
    D:\> cd D:\Development\Workspace\fmockup\vue-vite
    D:\Development\Workspace\fmockup\vue-vite> npm run build

    D:\Development\Workspace\fmockup\vue-vite> exit
    ```

## 参考

**React vs Vue**

https://qiita.com/yoichiwo7/items/236b6535695ea67b4fbe

**Axios から fetch API へリプレース**

https://zenn.dev/team_zenn/articles/migrated-from-axios-to-fetch-api