Laravel,Vue,Vuetifyの開発での不明点(初心者)
解決したいこと
・テーブルのレコードをクリックしても色がつかない。(選択自体はできている模様)
→こちらの問題は解決しました!@UMA9626さんありがとうございます!!
・ボタンを押しても入力フォームのダイアログが発生しない。(未解決)
初投稿、質問失礼いたします。
本腰を入れてプログラミングを開始してから1ヶ月程度の者です。
初心者用のテスト実施なのですが、人員管理システムのようなものを作成中です。
使用言語はLaravelとvue(vuetify)かと思います。
とても簡易な作りで、新規、更新、確認、削除の4ボタンの実装があり、
データベースの一覧を表示するテーブルには役職名、手当、有効無効のチェックボックスの3要素としております。
新規以外の項目はレコードを選択してからボタンを押す必要があるようにしたくて、
そのためにレコードを選択したら色が変わるようにしたいのですが、styleタグで実装したつもりですが、
有効に働いていないようです。
ブラウザ、ログファイルのエラー表記はなくなりましたが、思い描いている機能は実装できておりません。
お手数ですが、解決方法を教授いただけますと幸いです。
環境
visual studio code
Laravel
vue.js
vuetify 3.4.1
node v18.18.0
該当するソースコード
--Role.vue--
<template>
<v-container align="center" justify="center">
<v-card height="800" width="800" style="overflow-y: auto;">
<v-toolbar class="toolbar-color" dark flat>
<v-toolbar-title>ここは役職管理画面の予定</v-toolbar-title>
</v-toolbar>
<!-- RoleActions コンポーネントの追加 -->
<RoleActions @dialog-open="openDialog"></RoleActions>
<!-- RoleDialog コンポーネントの追加 -->
<!-- <RoleDialog :value="showDialog.NEW" @input="val => showDialog.NEW = val" :dialog-type="dialogTypes.NEW"
:form-data="formData"></RoleDialog> -->
<RoleDialog :show-dialog="showDialog.NEW" @update:show-dialog="val => showDialog.NEW = val"
:dialog-type="dialogTypes.NEW" :form-data="formData"></RoleDialog>
<RoleTable @role-selected="onRoleSelected" :roles="roles"></RoleTable>
</v-card>
</v-container>
</template>
<script>
import RoleTable from '@/Components/RoleTable.vue';
import RoleActions from '@/Components/RoleActions.vue';
import RoleDialog from '@/Components/RoleDialog.vue';
export default {
data() {
return {
roles: [],
selectedRole: null,
formData: {
},
dialogTypes: {
NEW: '新規',
UPDATE: '更新',
CONFIRM: '確認',
DELETE: '削除',
},
showDialog: {
NEW: false,
UPDATE: false,
CONFIRM: false,
DELETE: false,
},
};
},
mounted() {
this.fetchRoles();
},
methods: {
openDialog(dialogType) {
console.log("Opening dialog:", dialogType); // これを追加
if (dialogType in this.showDialog) {
this.showDialog[dialogType] = true;
this.dialogType = dialogType; // dialogType を設定
// 必要に応じて formData を初期化または設定
}
console.log("show dial:", this.showDialog); // これを追加
},
onRoleSelected(role) {
// RoleTable から受け取った役職情報を RoleDialog に渡す
console.log("Role selected in parent:", role);
this.selectedRole = role;
},
//選択したレコードを記憶するために作ったつもり
selectRole(role) {
this.selectedRole = role;
this.formData.roleName = role.RoleName;
this.formData.allowance = role.Allowance;
this.formData.isEnabled = role.IsEnabled === 1;
},
fetchRoles() {
axios.get('/api/role')
.then(response => {
this.roles = response.data;
})
.catch(error => {
console.error(error);
});
},
resetForm() {
this.formData = {
roleName: null,
allowance: null,
isEnabled: true,
};
},
},
components: {
RoleTable,
RoleActions,
RoleDialog,
}
};
</script>
<style>
.selected-row {
background-color: #ccca;
/* グレーの背景色を指定 */
}
</style>
--RoleTable.vue--
<template>
<!-- チェックボックスの表示 -->
<v-checkbox label="有効のみ表示" style="margin-left: 50px;" v-model="showOnlyEnabled"></v-checkbox>
<!-- テーブルの表示 -->
<v-table>
<thead>
<tr>
<th class="text-left">役職名</th>
<th class="text-left">手当</th>
<th class="text-left">有効</th>
</tr>
</thead>
<tbody>
<tr v-for="role in filteredRoles" :key="role.RoleSid" @click="selectRole(role)"
:class="{ 'selected-row': role === selectedRole }">
<td>{{ role.RoleName }}</td>
<td>{{ role.Allowance }}</td>
<td>
<v-checkbox v-model="role.IsEnabled" :true-value="1" :false-value="0" label="" :disabled="true"></v-checkbox>
</td>
</tr>
</tbody>
</v-table>
</template>
<script>
export default {
emits: ['roleSelected'], //必要かわからないけど、とりあえず
props: {
roles: Array, // roles をプロップスとして受け取る
// showOnlyEnabled: Boolean,
},
computed: {
filteredRoles() {
// "有効のみ表示" チェックボックスがオンの場合、IsEnabledが1の役職のみをフィルタリング
return this.showOnlyEnabled ? this.roles.filter(role => role.IsEnabled === 1) : this.roles;
},
},
data() {
return {
selectedRole: null,
showOnlyEnabled: false, // チェックボックスの状態
};
},
methods: {
//選択したレコードを記憶するために作ったつもり
selectRole(role) {
// this.selectedRole = role;
// 役職が選択されたときにカスタムイベントを発生させて親コンポーネントに役職情報を送信
// this.$emit('role-selected', role);
console.log("Selected role:", role);
this.$emit('roleSelected', role); // roleSelected イベントを発火
},
}
};
</script>
<style>
.selected-row {
background-color: #ccca;
/* グレーの背景色を指定 */
}
</style>
--RoleActions.vue--
<template>
<div class="role-actions">
<v-btn color="primary" @click="openDialog('NEW')">新規</v-btn>
<v-btn color="secondary" @click="openDialog('UPDATE')">更新</v-btn>
<v-btn color="info" @click="openDialog('CONFIRM')">確認</v-btn>
<v-btn color="error" @click="openDialog('DELETE')">削除</v-btn>
</div>
</template>
<script>
export default {
methods: {
openDialog(dialogType) {
console.log("Dialog type:", dialogType); // これを追加
this.$emit('dialog-open', dialogType);
},
},
};
</script>
<style scoped>
.role-actions {
margin: 20px;
}
</style>
--RoleDialog.vue--
<template>
<div class="role-actions">
<v-btn color="primary" @click="openDialog('NEW')">新規</v-btn>
<v-btn color="secondary" @click="openDialog('UPDATE')">更新</v-btn>
<v-btn color="info" @click="openDialog('CONFIRM')">確認</v-btn>
<v-btn color="error" @click="openDialog('DELETE')">削除</v-btn>
</div>
</template>
<script>
export default {
methods: {
openDialog(dialogType) {
console.log("Dialog type:", dialogType); // これを追加
this.$emit('dialog-open', dialogType);
},
},
};
</script>
<style scoped>
.role-actions {
margin: 20px;
}
</style>
その他
・一つのvueファイルのみで構成していたところ、思い描いた機能の実装ができまして、これを実務で活かせるように機能ごとのコンポーネント化を図ったところ、壁にぶつかっている状況です。
下記がコンポーネントを分ける前のソースコードです。
--Role.vue(全部込み)--
<template>
<v-container align="center" justify="center">
<v-card height="800" width="800" style="overflow-y: auto;">
<v-toolbar class="toolbar-color" dark flat>
<v-toolbar-title>ここは役職管理画面の予定</v-toolbar-title>
</v-toolbar>
<v-row align="center" justify="center" style="margin-top: 1%">
<!-- ボタンとダイアログを繰り返し生成 -->
<v-col v-for="dialogType in Object.values(dialogType)" :key="dialogType" cols="auto" style="margin: 1%">
<v-btn @click="openDialog(dialogType)" elevation="10" size="x-large">{{ dialogType }}</v-btn>
<v-dialog v-model="showDialog[dialogType]" max-width="500" >
<v-card :height="dialogType === dialogTypes.DELETE ? '150' : '400'" width="500">
<v-card-text v-if="dialogType == dialogTypes.DELETE">選択した役職を削除しますか?</v-card-text>
<v-card-text v-else>
<v-form ref="form">
<v-text-field v-model="formData.roleName" label="役職名" :readonly="dialogType === dialogTypes.CONFIRM" :rules="[rules.required]"
></v-text-field>
<v-text-field v-model="formData.allowance" label="手当" type="number"
:disabled="dialogType === dialogTypes.CONFIRM"
></v-text-field>
<v-checkbox v-model="formData.isEnabled" label="有効" :disabled="dialogType === dialogTypes.CONFIRM"
></v-checkbox>
</v-form>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn color="primary" @click="saveForm(dialogType)" v-if="dialogType !== dialogTypes.CONFIRM && dialogType !== dialogTypes.DELETE">{{ submitLabel }}</v-btn>
<v-btn color="primary" @click="deleteSelectedRole()" v-if="dialogType == dialogTypes.DELETE">はい</v-btn>
<v-btn color="error" @click="cancelForm(dialogType)">キャンセル</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
</v-row>
<v-checkbox label="有効のみ表示" style="margin-left: 50px;" v-model="showOnlyEnabled"></v-checkbox>
<!-- テーブルの表示 -->
<v-table>
<thead>
<tr>
<th class="text-left">役職名</th>
<th class="text-left">手当</th>
<th class="text-left">有効</th>
</tr>
</thead>
<tbody>
<tr v-for="role in filteredRoles" :key="role.RoleSid" @click="selectRole(role)"
:class="{ 'selected-row': role === selectedRole }">
<td>{{ role.RoleName }}</td>
<td>{{ role.Allowance }}</td>
<td>
<v-checkbox v-model="role.IsEnabled" :true-value="1" :false-value="0" label=""
:disabled="true"></v-checkbox>
</td>
</tr>
</tbody>
</v-table>
</v-card>
</v-container>
</template>
<script>
import dialogType from '../constants'; // constants.js をインポート
import axios from 'axios';
import rules from "../validation_rules";
export default {
data() {
return {
rules: rules,
roles: [], // ロールデータを保持するデータプロパティ
dialogType: dialogType.dialogType,
currentDialogType: null,
selectedRole: null, // 選択中の役職を保持するデータプロパティ
formData: {
// roleName: '',
// allowance: 0,
// isEnabled: true,
},
dialogTypes: {
NEW: '新規',
UPDATE: '更新',
CONFIRM: '確認',
DELETE: '削除',
},
showDialog: {
new: false,
update: false,
confirm: false,
delete: false,
},
showOnlyEnabled: false, // チェックボックスの状態
};
},
computed: {
filteredRoles() {
// "有効のみ表示" チェックボックスがオンの場合、IsEnabledが1の役職のみをフィルタリング
return this.showOnlyEnabled ? this.roles.filter(role => role.IsEnabled === 1) : this.roles;
},
},
mounted() {
this.fetchRoles();
},
methods: {
//選択したレコードを記憶するために作ったつもり
selectRole(role) {
this.selectedRole = role;
this.formData.roleName = role.RoleName;
this.formData.allowance = role.Allowance;
this.formData.isEnabled = role.IsEnabled === 1;
},
fetchRoles() {
axios.get('/api/role')
.then(response => {
this.roles = response.data;
})
.catch(error => {
console.error(error);
});
},
resetForm() {
this.formData = {
roleName: null,
allowance: null,
isEnabled: true,
};
},
// ダイアログを開くメソッド
// openDialog に選択された役職の確認を追加
openDialog(dialogType) {
if (dialogType !== this.dialogTypes.NEW && !this.selectedRole) {
// 役職が選択されていない場合の処理
alert("レコードを選択してください"); // または他のメッセージ表示方法を利用
return;
}
this.currentDialogType = dialogType;
this.resetForm(); // フォームデータをリセット
if (dialogType === this.dialogTypes.NEW) {
// 新規作成の場合の処理
this.dialogTitle = '新規作成';
this.submitLabel = '登録';
} else if (dialogType === this.dialogTypes.UPDATE) {
// 更新の場合の処理
this.dialogTitle = '更新';
this.submitLabel = '更新';
// フォームデータを選択したデータで初期化
this.formData.roleName = this.selectedRole.RoleName;
this.formData.allowance = this.selectedRole.Allowance;
this.formData.isEnabled = this.selectedRole.IsEnabled === 1;
} else if (dialogType === this.dialogTypes.CONFIRM) {
// 確認の場合の処理
// フォームデータを選択したデータで初期化
this.formData.roleName = this.selectedRole.RoleName;
this.formData.allowance = this.selectedRole.Allowance;
this.formData.isEnabled = this.selectedRole.IsEnabled === 1;
}
this.showDialog[dialogType] = true;
},
async saveForm(dialogType) {
console.log(this.$refs.form[0])
var result = await this.$refs.form[0].validate();
console.log(result)
if (!result.valid) {
return;
}
if (!this.formData.roleName) {
// 役職名が空の場合、アラートメッセージを表示
alert("役職名は必須です");
return;
}else if (this.formData.allowance < 0 || !this.formData.allowance) {
// 手当が0未満の場合、未入力の場合にアラートメッセージを表示
alert("手当は0以上の入力が必須です");
return;
}
if (this.currentDialogType === this.dialogTypes.NEW) {
// 新しい役職を登録する処理
// 数値に変換する
this.formData.allowance = Number(this.formData.allowance);
const newRole = {
RoleName: this.formData.roleName,
Allowance: this.formData.allowance,
IsEnabled: this.formData.isEnabled ? 1 : 0,
};
axios.post('/api/role', newRole)
.then(response => {
console.log(response);
this.fetchRoles();
this.cancelForm(this.currentDialogType);
})
.catch(error => {
console.error('エラーレスポンス', error.response); // エラーレスポンスをコンソールに表示
});
this.selectedRole = null; // 処理が終了したら選択を解除
} else if (this.currentDialogType === this.dialogTypes.UPDATE) {
const updatedRole = {
RoleName: this.formData.roleName,
Allowance: this.formData.allowance,
IsEnabled: this.formData.isEnabled ? 1 : 0,
};
// 更新対象の役職IDを取得するロジックを記述してください
const roleSid = this.selectedRole.RoleSid;
axios.put(`/api/role/${roleSid}`, updatedRole)
.then(response => {
console.log(response);
this.fetchRoles(); // 役職データを再取得
this.cancelForm(this.currentDialogType); // ダイアログを閉じる
})
.catch(error => {
console.error(error);
});
this.selectedRole = null; // 処理が終了したら選択を解除
}
},
// 例: 選択された役職を削除するメソッド
deleteSelectedRole() {
if (this.selectedRole) {
// 削除確認用ダイアログを非表示にする
this.showDialog[this.dialogTypes.DELETE] = false;
// 選択した役職を削除する処理を実行
const roleSid = this.selectedRole.RoleSid;
axios.delete(`/api/role/${roleSid}`)
.then(response => {
console.log(response);
this.fetchRoles(); // 役職データを再取得
this.cancelForm(this.dialogTypes.DELETE); // ダイアログを閉じる
})
.catch(error => {
console.error(error);
// 削除が失敗した場合のエラーハンドリングを追加
// ユーザーにエラーメッセージを表示するなど、適切な処理を行ってください
});
this.selectedRole = null; // 処理が終了したら選択を解除
}
},
cancelForm(dialogType) {
this.showDialog[dialogType] = false;
this.selectedRole = null; // 処理が終了したら選択を解除
},
// 各ダイアログを閉じる
closeDialogs() {
this.currentDialogType = null;
},
},
};
</script>
<style>
.selected-row {
background-color: #ccca;
/* グレーの背景色を指定 */
}
</style>
--constants.js--
/**
* APIのベースURL
*/
const API_BASE_USER = '/api';
import { createVuetify } from 'vuetify'
export const vuetify = createVuetify({
theme: {
//
},
})
export default {
/**
* メッセージ区分
*/
messageType: {
info: 1,
confirm: 2,
alert: 3,
},
// ダイアログ区分
dialogType: {
NEW: '新規',
UPDATE: '更新',
CONFIRM: '確認',
DELETE: '削除',
},
};
最後に
長文失礼いたしました。解決できるよう精進いたします。