Implementing Page Navigation and Dialogs in Vue 3
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:
- Import the dialog component:
import PermissionDialog from "./PermissionDialog.vue"; - Include the dialog component in the template:
<permission-dialog ref="dialogRef" @refreshList="fetchPermissionList"></permission-dialog> - Add an edit button that cals the edit method:
@click="editPermission(scope.row.id)" - 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:
- Define the visibility state:
const dialogVisible = ref(false); - Implement the add method:
openAddDialog() - Implement the edit method:
openEditDialog(id: string) - Implement the cancel method:
closeDialog(visible: boolean) - 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.