LoginSignup
0
0

Laravel,Vue,Vuetifyの開発での不明点(初心者)

Posted at

現状行き詰まっている箇所

・テーブルのレコードをクリックしても色がつかない。(選択自体はできている模様)
・ボタンを押しても入力フォームのダイアログが発生しない。


質問もさせていただきましたが、解決の過程も含めて記事にまとめたいと思い投稿いたしました。

本腰を入れてプログラミングを開始してから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: '削除',
  },
};


最後に

長文失礼いたしました。解決できるよう精進いたします。
解決できましたら、随時内容を更新しようと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0