Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Page Navigation and Dialogs in Vue 3

Tech May 18 2

Main Page Componnet

The main page displays a data table with search functionality and action buttons for CRUD operations. Here's how to implement it:

<template>
  <div class="app-container">
    <el-row :gutter="20">
      <el-col :span="20" :xs="24">
        <el-form :model="searchParams" ref="queryFormRef" :inline="true" label-width="80px">
          <el-form-item label="Permission Name">
            <el-input
                v-model="searchParams.map.permissionName"
                placeholder="Enter permission name"
                clearable
                style="width: 240px"
            />
          </el-form-item>
          <el-form-item label="Permission Code">
            <el-input
                v-model="searchParams.map.permissionCode"
                placeholder="Enter code"
                clearable
                style="width: 240px"
            />
          </el-form-item>

          <el-form-item label="Menu">
            <el-tree-select placeholder="Select menu" v-model="searchParams.map.menuId" :data="menuList"
                            :render-after-expand="false" clearable/>
          </el-form-item>

          <el-form-item>
            <el-button :color="appSettings.btnColor" icon="Search" @click="executeSearch" v-if="proxy.$checkAuth('PERMISSION_SEARCH')">Search</el-button>
            <el-button icon="Refresh" @click="resetSearch" v-if="proxy.$checkAuth('PERMISSION_RESET')">Reset</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button :color="appSettings.btnColor" plain icon="Plus" @click="addNewPermission" v-if="proxy.$checkAuth('PERMISSION_ADD')">Add New</el-button>
      </el-col>
    </el-row>
    <permission-dialog ref="dialogRef" @refreshList="fetchPermissionList"></permission-dialog>


    <div class="table-container">
      <el-table :data="permissionData" border stripe style="width: 100%"
                :header-cell-style="{'background-color':'#4991DD','color':'#ffffff'}" empty-text="No data available">
        <el-table-column prop="permissionName" label="Permission Name" align="center"/>
        <el-table-column prop="permissionCode" label="Permission Code" align="center"/>
        <el-table-column prop="menuName" label="Menu" align="center"/>
        <el-table-column prop="description" label="Description" align="center"/>
        <el-table-column prop="isActive" label="Active" align="center">
          <template #default="scope">
            {{ scope.row.isActive ? 'Yes' : 'No' }}
          </template>
        </el-table-column>
        <el-table-column label="Actions" width="200px" align="center">
          <template #default="scope">
            <el-tooltip content="Edit" placement="top" v-if="proxy.$checkAuth('PERMISSION_EDIT')">
              <el-button :color="appSettings.btnColor" circle plain size="small" icon="Edit"
                         @click="editPermission(scope.row.id)"></el-button>
            </el-tooltip>
            <el-tooltip content="Delete" placement="top" v-if="proxy.$checkAuth('PERMISSION_DELETE')">
              <el-button type="danger" circle plain size="small" icon="Delete"
                         @click="deletePermission(scope.row.id)"></el-button>
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination style="float: right;margin-top: 10px;" background layout="total, prev, pager, next" :locale="zhCn"
                     small :current-page="currentPage" :page-size="searchParams.pageSize" :total="totalCount"
                     @current-change="handlePageChange"/>
    </div>

  </div>
</template>

<script lang="ts" setup>
import {ElMessage, ElMessageBox} from 'element-plus'
import {getCurrentInstance, inject, onMounted, ref} from 'vue';
import zhCn from 'element-plus/lib/locale/lang/zh-cn';
import PermissionDialog from "./PermissionDialog.vue";

const appSettings: any = inject('appSettings');
const permissionData = ref([]);
const totalCount = ref(0);
const dialogRef = ref();
const dateRange = ref([]);
const currentPage = ref(1);
const searchParams = ref({
  pageNum: 1,
  pageSize: 10,
  map: {
    permissionName: "",
    permissionCode: "",
    menuId: ""
  }
});

const {proxy} = getCurrentInstance()

const menuList = ref([]);
const emit = defineEmits(['routerView'])

onMounted(() => {
  // Load menu dropdown options
  appSettings.axiosUtil.ajax_get(appSettings.baseUrl + "/api/system/menu/getDropdownList", function (res) {
    menuList.value = res.data;
  });
  // Load initial data
  fetchPermissionList();
});

const fetchPermissionList = () => {
  appSettings.axiosUtil.ajax_post(appSettings.baseUrl + "/api/system/permission/getList", searchParams.value, function (res: any) {
    permissionData.value = res.data
    totalCount.value = res.recordsTotal
  });
}

const addNewPermission = () => {
  dialogRef.value.openAddDialog();
}

// Navigate to dialog or another page
const editPermission = (id: string) => {
  dialogRef.value.openEditDialog(id);
}

const deletePermission = (id) => {
  ElMessageBox.confirm(
      'Are you sure you want to delete this permission?',
      'Confirmation',
      {
        confirmButtonText: 'Confirm',
        cancelButtonText: 'Cancel',
        type: 'warning',
      }
  ).then(() => {
    appSettings.axiosUtil.ajax_get(appSettings.baseUrl + "/api/system/permission/deleteById/" + id, function (res) {
      if (res.altStr == '1') {
        ElMessage.success('Deleted successfully!')
        fetchPermissionList()
      } else {
        fetchPermissionList()
      }
    });
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: 'Deletion canceled',
    })
  })
}

const executeSearch = () => {
  searchParams.value.pageNum = 1;
  fetchPermissionList()
}

const resetSearch = () => {
  currentPage.value = 1
  searchParams.value.pageNum = 1;
  searchParams.value.map = {
    permissionName: "",
    permissionCode: "",
    menuId: ""
  }
  dateRange.value = []
  fetchPermissionList()
}

const handlePageChange = (pageNumber) => {
  currentPage.value = pageNumber
  if (currentPage.value == 1) {
    searchParams.value.pageNum = 0
  } else {
    searchParams.value.pageNum = (currentPage.value - 1) * searchParams.value.pageSize
  }
  fetchPermissionList()
}
</script>

<style scoped>
.table-container {
  margin-top: 8px;
}
</style>

Key points for the main page:

  1. Import the dialog component: import PermissionDialog from "./PermissionDialog.vue";
  2. Include the dialog component in the template: <permission-dialog ref="dialogRef" @refreshList="fetchPermissionList"></permission-dialog>
  3. Add an edit button that cals the edit method: @click="editPermission(scope.row.id)"
  4. Impleemnt the edit method to open the dialog: dialogRef.value.openEditDialog(id);

Dialog Component

The dialog component handles adding and editing permissions. Here's the implementation:

<template>
  <el-dialog v-model="dialogVisible" :title="dialogTitle" width="30%" destroy-on-close draggable
             :close-on-click-modal="false">

    <el-form :model="entityData" :rules="validationRules" ref="formRef" label-width="90px">
      <el-row>
        <el-col :span="12">
          <el-form-item label="Permission Name" prop="permissionName">
            <el-input v-model="entityData.permissionName" clearable/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="Permission Code" prop="permissionCode">
            <el-input v-model="entityData.permissionCode" clearable :disabled="entityData.id != null"/>
          </el-form-item>
        </el-col>
      </el-row>

      <el-row>
        <el-col :span="12">
          <el-form-item label="Menu" prop="menuId">
            <el-tree-select v-model="entityData.menuId" :data="menuList" :render-after-expand="false" clearable/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="Active">
            <el-switch v-model="entityData.isActive"/>
          </el-form-item>
        </el-col>
      </el-row>

      <el-row>
        <el-col :span="24">
          <el-form-item label="Order" prop="sortOrder">
            <el-input-number v-model="entityData.sortOrder" class="mx-4" :min="0"/>
          </el-form-item>
        </el-col>
      </el-row>

      <el-row>
        <el-col :span="24">
          <el-form-item label="Description">
            <el-input v-model="entityData.description" type="textarea" clearable/>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>

    <template #footer>
        <span class="dialog-footer">
          <el-button :color="appSettings.btnColor" @click="saveData(formRef)">Save</el-button>
          <el-button @click="closeDialog(false)">Cancel</el-button>
        </span>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import {ref, reactive, inject, onMounted} from 'vue';
import type {FormInstance, FormRules} from 'element-plus'
import {ElMessage} from 'element-plus'

const appSettings: any = inject('appSettings');
const emit = defineEmits(['refreshList'])

const dialogVisible = ref(false);
const formRef = ref<FormInstance>();
const dialogTitle = ref('');
const entityData = ref({
  id: null,
  permissionName: null,
  permissionCode: null,
  menuId: null,
  sortOrder: 0,
  description: "",
  isActive: true
});

const openAddDialog = () => {
  dialogVisible.value = true
  dialogTitle.value = 'Add Permission'
  entityData.value = {
    id: null,
    permissionName: null,
    permissionCode: null,
    menuId: null,
    sortOrder: 0,
    description: "",
    isActive: true
  }
}

const menuList = ref([]);

onMounted(() => {
  appSettings.axiosUtil.ajax_get(appSettings.baseUrl + "/api/system/menu/getDropdownList", function (res) {
    menuList.value = res.data;
  });
});

// Submit data
const saveData = async (formEl: FormInstance | undefined) => {
  if (!formEl) return
  await formEl.validate((valid, fields) => {
    if (valid) {
      if (entityData.value.id != undefined && entityData.value.id != null) { // Edit mode
        appSettings.axiosUtil.ajax_post(appSettings.baseUrl + "/api/system/permission/update", entityData.value, function (res) {
          if (res.altStr == "1") {
            ElMessage.success('Updated successfully!')
            emit("refreshList");
            dialogVisible.value = false
          }
        });
      } else { // Add mode
        appSettings.axiosUtil.ajax_post(appSettings.baseUrl + "/api/system/permission/add", entityData.value, function (res) {
          if (res.altStr == "-1") {
            ElMessage.error('Permission code already exists!')
          } else if (res.altStr == "1") {
            ElMessage.success('Added successfully!')
            emit("refreshList");
            dialogVisible.value = false
          }
        });
      }
    } else {
      ElMessage.error('Please correct the form errors before submitting!')
    }
  })
};

const openEditDialog = (id: string) => {
  dialogVisible.value = true
  dialogTitle.value = 'Edit Permission'
  appSettings.axiosUtil.ajax_get(appSettings.baseUrl + "/api/system/permission/getById/" + id, function (res) {
    entityData.value = res.data
  });
};

// Form validation rules
const validationRules = reactive<FormRules>({
  permissionName: [
    {required: true, message: 'Please enter permission name', trigger: 'blur'},
  ],
  permissionCode: [
    {required: true, message: 'Please enter permission code', trigger: 'blur'},
  ],
  menuId: [
    {required: true, message: 'Please select a menu', trigger: 'change'},
  ],
  sortOrder: [
    {required: true, message: 'Please enter sort order', trigger: 'blur'},
  ],
})

// Cancel operation
const closeDialog = (visible: boolean) => {
  dialogVisible.value = visible
  entityData.value = {
    id: null,
    permissionName: null,
    permissionCode: null,
    menuId: null,
    sortOrder: 0,
    description: "",
    isActive: true
  }
  // Clear form validation
  formRef.value?.clearValidate()
}

// Expose methods to parent component
defineExpose({
  dialogVisible,
  openAddDialog,
  openEditDialog,
  closeDialog
});
</script>

<style scoped>
</style>

Key points for the dialog component:

  1. Define the visibility state: const dialogVisible = ref(false);
  2. Implement the add method: openAddDialog()
  3. Implement the edit method: openEditDialog(id: string)
  4. Implement the cancel method: closeDialog(visible: boolean)
  5. Expose methods to parent component: defineExpose({...})

This implementation provides a complete CRUD interface with a searchable table and modal dialogs for adding and editing records. The same pattern can be adapted for other entities in your application.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.