iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Software Development

全端工程師團隊的養成計畫系列 第 24

Day24 使用模板來進行改造範例:使用者群組功能授權實作(二)

  • 分享至 

  • xImage
  •  

早安~昨天已經完成前端範本的改造與後端 API 服務,今天要把兩者進行整合,直接進入主題吧!

建立 Store.ts 取得後端服務回應

  • 新增 EditGroupAppStore.ts 作為與後端溝通的橋樑。
  • CategoryAppsList:用來儲存從後端取回的資料。
  • fetchCategoryAppsList:向後端 API 服務發送請求。
  • updateSelectedApps:在全選特定功能群組時,更新 store 中的 CategoryAppsList 數值。
import { defineStore } from 'pinia';
// project imports
import { fetchWrapper  , type ActionRsp} from '@/utils/helpers/fetch-wrapper';
const baseUrl = `${import.meta.env.VITE_API_URL}`;
export interface CategoryApps {
    categoryID: string;
    categoryName: string;
    categoryApps: SysApp[];
}
interface SysApp {
    appID: number;
    appName: string;
    isSelected: boolean;
}

const CategoryAppsList: CategoryApps[] = [];
export const useEditGroupAppStore = defineStore({
      id: 'EditGroupAppStore',
      state: () => ({
        CategoryAppsList: [] as CategoryApps[],
        ActionRsp : {} as ActionRsp,
      }),
      actions: {
            async fetchCategoryAppsList(groupId: number) {
                try {
                    let apiUri : string = baseUrl;
                    apiUri = `${baseUrl}/CategoryApps?groupId=${groupId}`;
                    const respData: CategoryApps[] = await fetchWrapper.get(apiUri) as CategoryApps[];
                    this.CategoryAppsList = respData;
                } catch (error) {
                    console.error('Failed to fetch CategoryApps:', error);
                }
            },
            async saveGroupApps(Rgid: number, AppIds: number[]) {
            try {
                    let apiUri : string = baseUrl;
                    apiUri = `${baseUrl}`;
                    console.log("saveGroupApps(apiUri)", apiUri);
                    const respData: ActionRsp = await fetchWrapper.post(apiUri, {groupId, AppIds}) as ActionRsp;
                    this.ActionRsp = respData;

                } catch (error) {
                    console.error('Failed to fetch CategoryApps:', error);
            }
        },
        async updateSelectedApps( categoryID :string , isSelected : boolean) {
            // 修改 store 中的物件 CategoryAppsList 將下方的categoryApps的 isSelected 都改為 true
            this.CategoryAppsList.forEach((categoryApps) => {
                if (categoryApps.categoryID === categoryID) {
                    categoryApps.categoryApps.forEach((app) => {
                        app.isSelected = isSelected;
                    });
                }
            });
        }
      },
  });

動態呈現不同使用者群組的功能清單

回到需求面:管理者可以建立多個功能群組,並針對不同群組設定功能開放。這意味著 每次開啟編輯視窗時,都需要同步一次後端的資料。如圖24-1:
圖24-1
圖24-1:每當點選不同使用者群組時,前端會透過群組的 Id 向後端 API 請求,取得該群組已選取的功能清單。

技術實作說明

  1. 新增 watch,監測監聽 defineProps 傳遞的參數值變化觸發與後端的同步邏輯,各參數定義如下:
    1.1 DiaGroupApp : 視窗開啟/關閉狀態。
    2.2 selectedItem:傳遞的 群組ID 或是群組資料物件。
  2. 增加 fetchCategoryAppsList 方法,以 props.selectedItem.groupId 作為參數,向後端 API 請求該群組已選取的功能清單。
  3. 在對話框表頭顯示目前使用者群組名稱。
<script setup  lang="ts">
//DialogEditGroupApp.vue
   import { ref, watch, onMounted } from 'vue';
   import  { type CategoryApps  , useEditGroupAppStore} from '@/stores/EditGroupAppStore'; // 呼叫 backendApi store.ts
   import UiParentCard from '@/views/pages/Admin/GroupAppCard.vue';
   const props = defineProps({
       DiaGroupApp: Object,
       selectedItem: Object,
  });
  const editGroupAppStore = useEditGroupAppStore();
  const categoryAppsList = ref<CategoryApps[]>([]);
  const emit = defineEmits(['close']); // 父層是事件為關閉視窗
  const title   = ref('');
   watch(() => props.DiaGroupApp?.dialog,
       async (newValue) => {
       showEditGroupAppDia.value = newValue;
       title.value = props.selectedItem?.title || '';
       if (props.selectedItem && showEditGroupAppDia.value == true) {
           await editGroupAppStore.fetchCategoryAppsList(props.selectedItem.groupId);
           categoryAppsList.value = editGroupAppStore.CategoryAppsList;
      }
    }
  );
</script>
<template>
  <v-dialog v-model="showEditGroupAppDia"  persistent  scrollable max-width="1200px">
    <v-card >
      <v-card-item>
        <h3 class="text-h5 mb-3">群組【{{title}}】轄下功能設定</h3>
      </v-card-item>
      <v-divider></v-divider>
      <v-card-text>
         <v-row>
           <v-col v-for="category in categoryAppsList" :key="category.categoryID" class="mb-4" cols="3">
            <v-card></v-card>
            <UiParentCard :title="category.categoryName" :categoryID="category.categoryID">
              <div class="pb-0" v-for="(app, i) in category.categoryApps" :key="i" :value="app.appID" >
              <v-checkbox-btn color="success"  hide-details v-model="app.isSelected"  aria-label="checkbox">
                <template v-slot:label>
                  <div class="ms-2 text-darkText">
                    {{ app.appName }}
                  </div>
                </template>
              </v-checkbox-btn>
            </div>
            </UiParentCard>
          </v-col>
         </v-row>
      </v-card-text>
      <v-card-actions>
        <!-- to-do eslint error -->
        <v-btn color="primary" variant="flat" rounded="md" @click="">Save</v-btn>
        <v-btn variant="flat" rounded="md" @click="$emit('close')">Cancel</v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

驗證成果

圖24-2:點選 HR 使用者群組收到後端的回應內容。
圖24-2

圖24-3:點選 HR 使用者群組,未授權任功能。
圖24-3

圖24-4:透過資料庫建立幾筆模擬資料並點選 IT 使用者群組確認後端回應。
圖24-4

圖24-5:點選 IT 使用者群組,依據回應資料,顯示勾選的功能清單。
圖24-5

Ending remark

經過今天的整合,前後端已能順利串接,並依群組動態載入功能清單,成功做到已授權功能自動勾選。這也代表接下來的群組管理流程,已具備完整的基礎。


上一篇
Day23 Vue 模板改造實戰:使用者群組授權功能(一)
下一篇
Day25 資料匯出 Excel
系列文
全端工程師團隊的養成計畫25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言