Java道经 - 项目 - MyClub - 后台前端(四)
传送门:JP3-4-MyClub后台前端(一)
传送门:JP3-4-MyClub后台前端(二)
传送门:JP3-4-MyClub后台前端(三)
传送门:JP3-4-MyClub后台前端(四)
传送门:JP3-4-MyClub后台前端(五)
文章目录
S04. UMS用户管理
E01. UMS部门模块
武技:在 router/index.js 文件中开发全部相关页面路由配置
import Dept from '../views/ums/dept/Dept.vue';
import DeptInsert from '../views/ums/dept/DeptInsert.vue';
import DeptUpdate from '../views/ums/dept/DeptUpdate.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
{path: '/', name: 'Login', component: Login},
{
path: '/Main', name: 'Main', component: Main,
redirect: '/Dashboard',
children: [
{path: '/Dept', name: 'Dept', component: Dept},
{path: '/DeptInsert', name: 'DeptInsert', component: DeptInsert},
{path: '/DeptUpdate', name: 'DeptUpdate', component: DeptUpdate},
]
}
]
});
1. 部门列表
心法:部门列表页面
武技:开发部门列表页面 views/ums/dept/Dept.vue
<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'HomeFilled', label: '部门列表'},
];
// 数据头
const headItems = [
{type: 'ipt', span: 5, placeholder: '搜索部门名', callback: pageByTitle},
];
// 表格列
const columns = [
{label: '部门名称', prop: 'title'},
{label: '所在房间', prop: 'room.title'},
{label: '部门描述', prop: 'info', type: 'card', width: 520},
];
/* ==================== 分页查询 ==================== */
// 表格数据 + 分页数据 + 部门名称
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let deptTitle = ref();
async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
// 分页参数
let config = {
api: pageApi,
args: {module: 'dept'},
params: {pageNum, pageSize},
records, pageInfo
};
// 若房间名不为空,则附加为分页条件
if (deptTitle.value) config['params']['title'] = deptTitle.value;
// 发送分页请求
await myPage(config);
}
/* ==================== 搜索部门名 ==================== */
function pageByTitle(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || deptTitle.value) {
deptTitle.value = val;
page();
}
}
/* ==================== 删除成功回调 ==================== */
function deleteSuccess() {
ElMessage.success('删除成功');
page();
}
/* ==================== 报表打印 ==================== */
function downloadExcel() {
excelApi('/dept/excel', '部门报表');
}
/* ==================== 加载函数 ==================== */
onMounted(() => page());
</script>
<template>
<my-nav :items="navItems"/>
<my-head :items="headItems"/>
<my-table module="dept"
insert-page="/DeptInsert"
update-page="/DeptUpdate"
:records="records"
:columns="columns"
:delete-api="deleteApi"
:delete-batch-api="deleteBatchApi"
:delete-callback="deleteSuccess"
:excel-api="downloadExcel"
:pageInfo="pageInfo"/>
</template>
<style scoped lang="scss"></style>
2. 添加部门
心法:添加部门页面
武技:开发添加部门页面
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {onMounted, reactive, ref} from "vue";
import {insertApi, listApi} from "../../../api/axios.js";
import {getResponseData} from "../../../request/index.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 所在房间下拉菜单选项
let roomOptions = ref([]);
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'HomeFilled', label: '部门列表', url: '/Dept'},
{icon: 'Plus', label: '添加新部门'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '部门名称', prop: 'title', required: true, span: 12},
{span: 12, hidden: true},
{label: '所在房间', prop: 'fkRoomId', required: true, type: 'select', options: roomOptions.value, span: 12},
{label: '部门描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({});
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 添加成功后 ==================== */
function insertSuccess() {
ElMessage.success('添加记录成功!');
setTimeout(() => router.push('/Dept'), 1000);
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
// 查询全部房间并添加到下拉菜单选项中
Object.values(getResponseData(await listApi({module: 'room'}))).forEach(room => {
roomOptions.value.push({label: room['title'], value: room['id']});
});
});
</script>
<template>
<my-nav :items="navItems"/>
<el-card v-if="roomOptions.length > 0" class="dept-insert-card" header="添加新部门">
<my-form type="insert"
:items="items"
:params="params"
:rules="rules"
:api="insertApi"
:args="{module: 'dept'}"
:callback="insertSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.dept-insert-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
3. 修改部门
心法:修改部门页面
武技:开发修改部门页面 views/ums/dept/DeptUpdate.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {onMounted, reactive, ref} from "vue";
import {listApi, updateApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import {getResponseData} from "../../../request/index.js";
import router from "../../../router";
// 部门记录
let dept = JSON.parse(sessionStorage.getItem('row'));
// 所在房间下拉菜单选项
let roomOptions = ref([]);
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'HomeFilled', label: '部门列表', url: '/Dept'},
{icon: 'Edit', label: '修改部门'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '部门名称', prop: 'title', required: true, span: 12},
{span: 12, hidden: true},
{label: '所在房间', prop: 'fkRoomId', required: true, type: 'select', options: roomOptions.value, span: 12},
{label: '部门描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(dept);
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 修改成功后 ==================== */
function updateSuccess() {
ElMessage.success('修改记录成功!');
setTimeout(() => router.push('/Dept'), 1000);
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
// 查询全部房间并添加到下拉菜单选项中
Object.values(getResponseData(await listApi({module: 'room'}))).forEach(room => {
roomOptions.value.push({label: room['title'], value: room['id']});
});
});
</script>
<template>
<my-nav :items="navItems"/>
<el-card v-if="roomOptions.length > 0" class="dept-update-card" header="修改部门信息">
<my-form type="update"
:items="items"
:params="params"
:rules="rules"
:api="updateApi"
:args="{module: 'dept'}"
:callback="updateSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.dept-update-card{
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
E02. UMS员工模块
武技:在 router/index.js 文件中开发全部相关页面路由配置
import Emp from '../views/ums/emp/Emp.vue';
import EmpInsert from '../views/ums/emp/EmpInsert.vue';
import EmpUpdate from '../views/ums/emp/EmpUpdate.vue';
import EmpUpdateRoles from "../views/ums/emp/EmpUpdateRoles.vue";
const router = createRouter({
history: createWebHashHistory(),
routes: [
{path: '/', name: 'Login', component: Login},
{
path: '/Main', name: 'Main', component: Main,
redirect: '/Dashboard',
children: [
{path: '/Emp', name: 'Emp', component: Emp},
{path: '/EmpInsert', name: 'EmpInsert', component: EmpInsert},
{path: '/EmpUpdate', name: 'EmpUpdate', component: EmpUpdate},
{path: '/EmpUpdateRoles ', name: 'EmpUpdateRoles ', component: EmpUpdateRoles },
]
}
]
});
1. 员工列表
心法:员工列表页面
武技:开发员工列表页面 views/ums/emp/Emp.vue
<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {getResponseData, myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, listApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
import {dateFormat, genderFormat} from "../../../util/index.js";
import {PROJECT_INFO} from "../../../const/index.js";
import router from "../../../router/index.js";
// 所在部门下拉菜单选项
let deptOptions = ref([]);
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'User', label: '员工列表'}
];
// 数据头
const headItems = [
{type: 'ipt', span: 5, placeholder: '搜索登录账号', callback: pageByUsername},
{type: 'ipt', span: 5, placeholder: '搜索手机号码', callback: pageByPhone},
{type: 'ipt', span: 5, placeholder: '搜索身份证号', callback: pageByIdcard},
{type: 'ipt', span: 5, placeholder: '搜索真实姓名', callback: pageByRealname},
{type: 'opt', span: 4, placeholder: '搜索部门', callback: pageByDeptId, options: deptOptions.value},
];
// 表格列
const columns = [
{label: '员工头像', prop: 'avatar', type: 'img', minio: minio},
{label: '真实姓名', prop: 'realname', width: 90},
{label: '所在部门', prop: 'dept.title', width: 130},
{label: '员工性别', prop: 'gender', type: 'tag', tagTypeFn: e => e === 0 ? 'danger' : e === 1 ? 'primary' : 'default', format: genderFormat, width: 90},
{label: '员工描述', prop: 'info', type: 'card'},
{label: '登录账号', prop: 'username', width: 130},
{label: '手机号码', prop: 'phone', width: 90},
{label: '身份证号', prop: 'idcard', width: 140},
{label: '微信号码', prop: 'wechat'},
{label: '电子邮箱', prop: 'email'},
{label: '员工年龄', prop: 'age', width: 90},
{label: '所在省份', prop: 'province', width: 90},
{label: '现居住地', prop: 'address', type: 'card'},
{label: '入职日期', prop: 'hiredate', type: 'date', format: dateFormat, width: 110},
];
// 按钮列
const buttons = [
{label: '重设角色', icon: 'Edit', callback: toEmpUpdateRoles},
];
/* ==================== 重设角色 ==================== */
function toEmpUpdateRoles(row) {
router.push({
path: '/EmpUpdateRoles', query: {
empId: row['id'],
realname: row['realname']
}
});
}
/* ==================== 员工头像 ==================== */
function minio(src) {
return PROJECT_INFO.minioHost + '/avatar/' + src;
}
/* ==================== 分页查询 ==================== */
// 表格数据 + 分页数据 + 登录账号 + 手机号码 + 身份证号 + 真实姓名 + 所在部门ID
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let username = ref();
let phone = ref();
let idcard = ref();
let realname = ref();
let fkDeptId = ref();
async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
// 分页参数
let config = {
api: pageApi,
args: {module: 'emp'},
params: {pageNum, pageSize},
records, pageInfo
};
// 若登录账号不为空,则附加为分页条件
if (username.value) config['params']['username'] = username.value;
// 若手机号码不为空,则附加为分页条件
if (phone.value) config['params']['phone'] = phone.value;
// 若身份证号不为空,则附加为分页条件
if (idcard.value) config['params']['idcard'] = idcard.value;
// 若真实姓名不为空,则附加为分页条件
if (realname.value) config['params']['realname'] = realname.value;
// 若所在部门ID不为空,则附加为分页条件
if (fkDeptId.value) config['params']['fkDeptId'] = fkDeptId.value;
// 发送分页请求
await myPage(config);
}
/* ==================== 搜索登录账号 ==================== */
function pageByUsername(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || username.value) {
username.value = val;
page();
}
}
/* ==================== 搜索手机号码 ==================== */
function pageByPhone(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || phone.value) {
phone.value = val;
page();
}
}
/* ==================== 搜索身份证号 ==================== */
function pageByIdcard(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || idcard.value) {
idcard.value = val;
page();
}
}
/* ==================== 搜索真实姓名 ==================== */
function pageByRealname(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || realname.value) {
realname.value = val;
page();
}
}
/* ==================== 搜索所在部门 ==================== */
function pageByDeptId(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || fkDeptId.value) {
fkDeptId.value = val;
page();
}
}
/* ==================== 删除成功回调 ==================== */
function deleteSuccess() {
ElMessage.success('删除成功!');
page();
}
/* ==================== 报表打印 ==================== */
function downloadExcel() {
excelApi('/emp/excel', '员工报表');
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
await page();
// 查询全部部门并添加到下拉菜单选项中
Object.values(getResponseData(await listApi({module: 'dept'}))).forEach(dept => {
deptOptions.value.push({label: dept['title'], value: dept['id']});
});
});
</script>
<template>
<my-nav :items="navItems"/>
<my-head :items="headItems" v-if="deptOptions.length > 0"/>
<my-table module="emp"
insert-page="/EmpInsert"
update-page="/EmpUpdate"
:records="records"
:columns="columns"
:buttons="buttons"
:delete-api="deleteApi"
:delete-batch-api="deleteBatchApi"
:delete-callback="deleteSuccess"
:excel-api="downloadExcel"
:pageInfo="pageInfo"/>
</template>
<style scoped lang="scss"></style>
2. 添加员工
心法:添加员工页面
武技:开发添加员工页面 views/ums/emp/EmpInsert.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {onMounted, reactive, ref} from "vue";
import {insertApi, listApi} from "../../../api/axios.js";
import {getResponseData} from "../../../request/index.js";
import {DEFAULT_PASSWORD, RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 所在部门下拉菜单选项
let deptOptions = ref([]);
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'User', label: '员工列表', url: '/Emp'},
{icon: 'Plus', label: '添加新员工'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '登录账号', prop: 'username', required: true, span: 12},
{label: '真实姓名', prop: 'realname', required: true, span: 12},
{label: '手机号码', prop: 'phone', required: true, span: 12},
{label: '身份证号', prop: 'idcard', required: true, span: 12},
{label: '微信号码', prop: 'wechat', required: true, span: 12},
{label: '电子邮箱', prop: 'email', required: true, span: 12},
{label: '所在部门', prop: 'fkDeptId', required: true, type: 'select', options: deptOptions.value, span: 12},
{label: '入职日期', prop: 'hiredate', required: true, type: 'datetime', span: 12},
{label: '现居住地', prop: 'address', required: true, type: 'textarea', rows: 2},
{label: '员工描述', prop: 'info', type: 'textarea', rows: 7},
]);
let params = reactive({password: DEFAULT_PASSWORD});
let rules = {
realname: RULE.REALNAME,
phone: RULE.PHONE,
email: RULE.EMAIL,
address: RULE.ADDRESS,
idcard: RULE.IDCARD,
info: RULE.INFO,
};
/* ==================== 添加成功后 ==================== */
function insertSuccess() {
ElMessage.success('添加成功!');
setTimeout(() => router.push('/Emp'), 1000);
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
// 查询全部部门并添加到下拉菜单选项中
Object.values(getResponseData(await listApi({module: 'dept'}))).forEach(dept => {
deptOptions.value.push({label: dept['title'], value: dept['id']});
});
});
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="emp-insert-card" header="添加新员工">
<my-form v-if="deptOptions.length > 0"
type="insert"
:items="items"
:params="params"
:rules="rules"
:api="insertApi"
:args="{module: 'emp'}"
:callback="insertSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.emp-insert-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
3. 修改员工
心法:修改员工页面
武技:开发修改员工页面 views/ums/emp/EmpUpdate.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import MyUpload from "../../../components/MyUpload.vue";
import {onMounted, reactive, ref} from "vue";
import {listApi, updateApi} from "../../../api/axios.js";
import {getResponseData} from "../../../request/index.js";
import {GENDER_OPTIONS, RULE} from "../../../const";
import {ElMessage} from "element-plus";
import {UPLOAD_AVATAR_URL} from "../../../api/emp.js";
import router from "../../../router";
// 员工记录
let emp = JSON.parse(sessionStorage.getItem('row'));
// 所在部门下拉菜单选项
let deptOptions = ref([]);
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'User', label: '员工列表', url: '/Emp'},
{icon: 'Edit', label: '修改员工'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '登录账号', prop: 'username', disabled: true, span: 12},
{label: '真实姓名', prop: 'realname', required: true, span: 12},
{label: '手机号码', prop: 'phone', required: true, span: 12},
{label: '身份证号', prop: 'idcard', required: true, span: 12},
{label: '微信号码', prop: 'wechat', required: true, span: 12},
{label: '电子邮箱', prop: 'email', required: true, span: 12},
{label: '员工性别', prop: 'gender', required: true, type: 'select', options: GENDER_OPTIONS, span: 12},
{label: '员工年龄', prop: 'age', required: true, type: 'number', span: 12},
{label: '所在部门', prop: 'fkDeptId', required: true, type: 'select', options: deptOptions.value, span: 12},
{label: '入职日期', prop: 'hiredate', required: true, type: 'datetime', span: 12},
{label: '现居住地', prop: 'address', required: true, type: 'textarea', rows: 2},
{label: '员工描述', prop: 'info', type: 'textarea', rows: 7},
]);
let params = reactive(emp);
let rules = {
realname: RULE.REALNAME,
phone: RULE.PHONE,
email: RULE.EMAIL,
address: RULE.ADDRESS,
idcard: RULE.IDCARD,
info: RULE.INFO,
};
/* ==================== 修改成功后 ==================== */
function updateSuccess() {
ElMessage.success('修改记录成功!');
setTimeout(() => router.push('/Emp'), 1000);
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
// 查询全部部门并添加到下拉菜单选项中
Object.values(getResponseData(await listApi({module: 'dept'}))).forEach(dept => {
deptOptions.value.push({label: dept['title'], value: dept['id']});
});
});
</script>
<template>
<my-nav :items="navItems"/>
<el-row :gutter="30" class="emp-update-row">
<el-col :span="16">
<el-card class="emp-update-card" header="修改基本信息">
<my-form v-if="deptOptions.length > 0"
type="update"
:items="items"
:params="params"
:rules="rules"
:api="updateApi"
:args="{module: 'emp'}"
:callback="updateSuccess"/>
</el-card>
</el-col>
<el-col :span="8">
<el-card header="上传员工头像">
<my-upload :url="UPLOAD_AVATAR_URL + '/' + emp['id']"
name="avatarFile"
:callback="updateSuccess"
:autoUpload="true"/>
</el-card>
</el-col>
</el-row>
</template>
<style scoped lang="scss">
.emp-update-row {
margin: 65px auto 0; // 外边距
}
</style>
4. 重设角色
心法:重设员工角色页面
武技:开发重设员工角色页面 views/ums/emp/EmpUpdateRoles.vue
<script setup>
import MyNav from "../../../components/MyNav.vue";
import {getResponseData} from "../../../request";
import {onMounted, ref} from "vue";
import {listApi} from "../../../api/axios.js";
import {listByEmpIdApi, updateByEmpIdApi} from "../../../api/role.js";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 获取路由中的员工主键和真实姓名
let empId = router.currentRoute.value.query['empId'];
let realname = router.currentRoute.value.query['realname'];
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'User', label: '员工列表', url: '/Emp'},
{icon: 'Edit', label: '为员工重设角色'},
];
// 全部角色 + 我的角色主键数组
let allRoles = ref([]);
let roleIds = ref([]);
/* ==================== 确认修改角色 ==================== */
async function updateRoles() {
let data = getResponseData(await updateByEmpIdApi(empId, roleIds.value));
if (data) {
ElMessage.success('角色重设成功,下次登录生效!');
}
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
// 查询全部角色
Object.values(getResponseData(await listApi({module: 'role'}))).forEach(role => {
allRoles.value.push({label: role['title'], key: role['id']});
});
// 查询该员工的角色
Object.values(getResponseData(await listByEmpIdApi(empId))).forEach(role => {
roleIds.value.push(role['id']);
});
});
</script>
<template>
<div class="emp-roles-body">
<my-nav :items="navItems"/>
<el-transfer class="emp-roles-transfer"
filterable filter-placeholder="输入关键字"
v-if="allRoles.length > 0"
v-model="roleIds"
:data="allRoles"
:titles="['全部可选角色', '【' + realname+ '】已选角色']"
:props="{key: 'key', label: 'label'}"
:button-texts="['移除', '添加']">
<template #left-footer>
<el-text class="mx-1" type="info">tips: 请重新选择该员工的角色!</el-text>
</template>
<template #right-footer>
<el-button type="primary" @click="updateRoles" size="small">确认修改</el-button>
</template>
</el-transfer>
</div>
</template>
<style scoped lang="scss">
.emp-roles-body {
text-align: center; // 内容居中
.emp-roles-transfer {
margin-top: 65px; // 上外边距
}
}
:deep(.el-transfer-panel) {
width: 300px; // 宽度
}
:deep(.el-transfer-panel__body) {
height: 400px; // 高度
}
:deep(.el-transfer-panel__footer) {
text-align: center; // 内容居中
}
</style>
E03. UMS角色模块
武技:在 router/index.js 文件中开发全部相关页面路由配置
import Role from '../views/ums/role/Role.vue';
import RoleInsert from '../views/ums/role/RoleInsert.vue';
import RoleUpdate from '../views/ums/role/RoleUpdate.vue';
import RoleUpdateMenus from '../views/ums/role/RoleUpdateMenus.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
{path: '/', name: 'Login', component: Login},
{
path: '/Main', name: 'Main', component: Main,
redirect: '/Dashboard',
children: [
{path: '/Role', name: 'Role', component: Role},
{path: '/RoleInsert', name: 'RoleInsert', component: RoleInsert},
{path: '/RoleUpdate', name: 'RoleUpdate', component: RoleUpdate},
{path: '/RoleUpdateMenus', name: 'RoleUpdateMenus', component: RoleUpdateMenus},
]
}
]
});
1. 角色列表
心法:角色列表页面
武技:开发角色列表页面 views/ums/role/Role.vue
<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
import router from "../../../router/index.js";
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'UserFilled', label: '角色列表'}
];
// 数据头
const headItems = [
{type: 'ipt', span: 5, placeholder: '搜索角色标题', callback: pageByTitle},
];
// 表格列
const columns = [
{label: '角色标题', prop: 'title'},
{label: '角色描述', prop: 'info', type: 'card', width: 520},
];
// 按钮列
const buttons = [
{label: '重设菜单', icon: 'Edit', callback: toRoleUpdateMenus},
];
/* ==================== 重设菜单 ==================== */
function toRoleUpdateMenus(row) {
router.push({
path: '/RoleUpdateMenus', query: {
roleId: row['id'],
roleTitle: row['title']
}
});
}
/* ==================== 分页查询 ==================== */
// 表格数据 + 分页数据 + 角色标题
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let roleTitle = ref();
async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
// 分页参数
let config = {
api: pageApi,
args: {module: 'role'},
params: {pageNum, pageSize},
records, pageInfo
};
// 若角色标题不为空,则附加为分页条件
if (roleTitle.value) config['params']['title'] = roleTitle.value;
// 发送分页请求
await myPage(config);
}
/* ==================== 搜索角色标题 ==================== */
function pageByTitle(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || roleTitle.value) {
roleTitle.value = val;
page();
}
}
/* ==================== 删除成功回调 ==================== */
function deleteSuccess() {
ElMessage.success('删除成功!');
page();
}
/* ==================== 报表打印 ==================== */
function downloadExcel() {
excelApi('/role/excel', '角色报表');
}
/* ==================== 加载函数 ==================== */
onMounted(() => page());
</script>
<template>
<my-nav :items="navItems"/>
<my-head :items="headItems"/>
<my-table module="role"
insert-page="/RoleInsert"
update-page="/RoleUpdate"
:records="records"
:columns="columns"
:buttons="buttons"
:delete-api="deleteApi"
:delete-batch-api="deleteBatchApi"
:delete-callback="deleteSuccess"
:excel-api="downloadExcel"
:pageInfo="pageInfo"/>
</template>
<style scoped lang="scss"></style>
2. 添加角色
心法:添加角色页面
武技:开发添加角色页面 views/ums/role/RoleInsert.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../api/axios.js";
import {DEFAULT_PASSWORD, RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'UserFilled', label: '角色列表', url: '/Role'},
{icon: 'Plus', label: '添加新角色'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '角色标题', prop: 'title', required: true, span: 12},
{hidden: true, span: 12},
{label: '角色描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({password: DEFAULT_PASSWORD});
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 添加成功后 ==================== */
function insertSuccess() {
ElMessage.success('添加成功!');
setTimeout(() => router.push('/Role'), 1000);
}
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="role-insert-card" header="添加新角色">
<my-form type="insert"
:items="items"
:params="params"
:rules="rules"
:api="insertApi"
:args="{module: 'role'}"
:callback="insertSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.role-insert-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
3. 修改角色
心法:修改角色页面
武技:开发修改角色页面 views/ums/role/RoleUpdate.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {updateApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 角色记录
let role = JSON.parse(sessionStorage.getItem('row'));
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'UserFilled', label: '角色列表', url: '/Role'},
{icon: 'Edit', label: '修改角色'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '角色标题', prop: 'title', required: true, span: 12},
{hidden: true, span: 12},
{label: '角色描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(role);
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 修改成功后 ==================== */
function updateSuccess() {
ElMessage.success('修改记录成功!');
setTimeout(() => router.push('/Role'), 1000);
}
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="role-update-card" header="修改角色信息">
<my-form type="update"
:items="items"
:params="params"
:rules="rules"
:api="updateApi"
:args="{module: 'role'}"
:callback="updateSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.role-update-card{
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
4. 重设菜单
心法:重设菜单页面
武技:开发重设菜单页面 views/ums/role/RoleUpdateMenus.vue
<script setup>
import MyNav from "../../../components/MyNav.vue";
import {getResponseData} from "../../../request";
import {onMounted, ref} from "vue";
import {listApi} from "../../../api/axios.js";
import {listByRoleIdApi, updateByRoleIdApi} from "../../../api/menu.js";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 获取路由中的角色主键和角色标题
let roleId = router.currentRoute.value.query['roleId'];
let roleTitle = router.currentRoute.value.query['roleTitle'];
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'UserFilled', label: '角色列表', url: '/Role'},
{icon: 'Edit', label: '为角色重设菜单'},
];
// 全部菜单 + 我的菜单主键数组
let allMenus = ref([]);
let menuIds = ref([]);
// 菜单的父子关系
let idToPidMap = {};
/* ==================== 确认修改菜单 ==================== */
/**
* 处理选中的菜单ID列表
*
* 1. 遍历处理选中的菜单ID列表。
* 2. 将选中的菜单ID追加到临时数组 result 中。
* 3. 根据菜单的父子关系 idToPidMap 对象获取该菜单的父菜单ID。
* 4. 若临时数组 result 中已存在相同的父菜单ID,则不会重复追加。
* 5. 若临时数组 result 中不存在相同的父菜单ID,则追加到临时数组 result 中。
*
* @param menuIds 选中的菜单列表
* @return result 处理后的菜单ID列表,包括全部子菜单的ID和对应父菜单的ID,不存在重复项。
*/
function buildMenuIds(menuIds) {
let result = [];
for (let i in menuIds) {
result.push(menuIds[i]);
let parentMenuId = idToPidMap[menuIds[i]];
if (result.indexOf(parentMenuId) === -1) {
result.push(parentMenuId);
}
}
return result;
}
async function updateMenus() {
let data = getResponseData(await updateByRoleIdApi(roleId, buildMenuIds(menuIds.value)));
if (data) {
ElMessage.success('菜单重设成功,下次登录生效!');
}
}
/* ==================== 加载函数 ==================== */
onMounted(async () => {
// 查询全部菜单
Object.values(getResponseData(await listApi({module: 'menu'}))).forEach(menu => {
// 记录菜单的父子关系: {id01: pid01, id02: pid02 .. }
idToPidMap[menu['id']] = menu['pid'];
// 组装 ElTransfer 数据
if (menu['pid'] !== 0) {
allMenus.value.push({label: menu['parentTitle'] + ' / ' + menu['title'], key: menu['id']});
}
});
// 查询该角色的菜单
Object.values(getResponseData(await listByRoleIdApi(roleId))).forEach(menu => {
menuIds.value.push(menu['id']);
});
});
</script>
<template>
<div class="role-menus-body">
<my-nav :items="navItems"/>
<el-transfer class="role-menus-transfer"
filterable filter-placeholder="输入关键字"
v-if="allMenus.length > 0"
v-model="menuIds"
:data="allMenus"
:titles="['全部可选菜单', '【' + roleTitle + '】已选菜单']"
:props="{key: 'key', label: 'label'}"
:button-texts="['移除', '添加']">
<template #left-footer>
<el-text class="mx-1" type="info">tips: 请重新选择该角色的菜单!</el-text>
</template>
<template #right-footer>
<el-button type="primary" @click="updateMenus" size="small">确认修改</el-button>
</template>
</el-transfer>
</div>
</template>
<style scoped lang="scss">
.role-menus-body {
text-align: center; // 内容居中
.role-menus-transfer {
margin-top: 65px; // 上外边距
}
}
:deep(.el-transfer-panel) {
width: 300px; // 宽度
}
:deep(.el-transfer-panel__body) {
height: 400px; // 高度
}
:deep(.el-transfer-panel__footer) {
text-align: center; // 内容居中
}
</style>
E04. UMS菜单模块
武技:在 router/index.js 文件中开发全部相关页面路由配置
import Menu from '../views/ums/menu/Menu.vue';
import MenuInsert from '../views/ums/menu/MenuInsert.vue';
import MenuUpdate from '../views/ums/menu/MenuUpdate.vue';
import SubMenu from '../views/ums/menu/subMenu/SubMenu.vue';
import SubMenuInsert from '../views/ums/menu/subMenu/SubMenuInsert.vue';
import SubMenuUpdate from '../views/ums/menu/subMenu/SubMenuUpdate.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
{path: '/', name: 'Login', component: Login},
{
path: '/Main', name: 'Main', component: Main,
redirect: '/Dashboard',
children: [
{path: '/Menu', name: 'Menu', component: Menu},
{path: '/MenuInsert', name: 'MenuInsert', component: MenuInsert},
{path: '/MenuUpdate', name: 'MenuUpdate', component: MenuUpdate},
{path: '/SubMenu', name: 'SubMenu', component: SubMenu},
{path: '/SubMenuInsert', name: 'SubMenuInsert', component: SubMenuInsert},
{path: '/SubMenuUpdate', name: 'SubMenuUpdate', component: SubMenuUpdate},
]
}
]
});
1. 父菜单列表
心法:父菜单列表页面
武技:开发父菜单列表页面 views/ums/menu/Menu.vue
<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
import router from "../../../router/index.js";
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'Menu', label: '菜单列表(父菜单)'}
];
// 数据头
const headItems = [
{type: 'ipt', span: 5, placeholder: '搜索父菜单名', callback: pageByTitle},
];
// 表格列
const columns = [
{label: '菜单图标', prop: 'icon', type: 'icon', width: 90},
{label: '菜单名称', prop: 'title', width: 120},
{label: '菜单描述', prop: 'info', type: 'card', width: 520},
];
// 按钮列
const buttons = [
{label: '下级菜单', icon: 'Menu', callback: toSubMenu},
];
/* ==================== 跳转下级菜单页面 ==================== */
function toSubMenu(row) {
sessionStorage.setItem('pid', row['id']);
sessionStorage.setItem('parentTitle', row['title']);
router.push('/SubMenu');
}
/* ==================== 分页查询 ==================== */
// 表格数据 + 分页数据 + 菜单名称
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let menuTitle = ref();
async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
// 分页参数(额外添加 pid=0 保证仅查询父菜单)
let config = {
api: pageApi,
args: {module: 'menu'},
params: {pageNum, pageSize, pid: 0},
records, pageInfo
};
// 若菜单名不为空,则附加为分页条件
if (menuTitle.value) config['params']['title'] = menuTitle.value;
// 发送分页请求
await myPage(config);
}
/* ==================== 搜索菜单名 ==================== */
function pageByTitle(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || menuTitle.value) {
menuTitle.value = val;
page();
}
}
/* ==================== 删除成功回调 ==================== */
function deleteSuccess() {
ElMessage.success('删除成功!');
page();
}
/* ==================== 报表打印 ==================== */
function downloadExcel() {
excelApi('/menu/excel', '菜单报表');
}
/* ==================== 加载函数 ==================== */
onMounted(() => page());
</script>
<template>
<my-nav :items="navItems"/>
<my-head :items="headItems"/>
<my-table module="menu"
insert-page="/MenuInsert"
update-page="/MenuUpdate"
:records="records"
:columns="columns"
:buttons="buttons"
:delete-api="deleteApi"
:delete-batch-api="deleteBatchApi"
:delete-callback="deleteSuccess"
:excel-api="downloadExcel"
:pageInfo="pageInfo"/>
</template>
<style scoped lang="scss"></style>
2. 添加父菜单
心法:添加父菜单页面
武技:开发添加父菜单页面 views/ums/menu/MenuInsert.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理', url: '/Dept'},
{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
{icon: 'Plus', label: '添加新菜单(父菜单)'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '菜单名称', prop: 'title', required: true, span: 12},
{span: 12, hidden: true},
{label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
{label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({pid: 0, url: '/'});
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 添加成功后 ==================== */
function insertSuccess() {
ElMessage.success('添加记录成功!');
setTimeout(() => router.push('/Menu'), 1000);
}
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="menu-insert-card" header="添加新菜单(父菜单)">
<my-form type="insert"
:items="items"
:params="params"
:rules="rules"
:api="insertApi"
:args="{module: 'menu'}"
:callback="insertSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.menu-insert-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
3. 修改父菜单
心法:修改父菜单页面
武技:开发修改父菜单页面 views/ums/menu/MenuUpdate.vue
<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {updateApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";
// 菜单记录
let menu = JSON.parse(sessionStorage.getItem('row'));
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理', url: '/Dept'},
{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
{icon: 'Edit', label: '修改菜单(父菜单)'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '菜单名称', prop: 'title', required: true, span: 12},
{span: 12, hidden: true},
{label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
{label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(menu);
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 修改成功后 ==================== */
function updateSuccess() {
ElMessage.success('修改记录成功!');
setTimeout(() => router.push('/Menu'), 1000);
}
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="menu-update-card" header="修改菜单(父菜单)">
<my-form type="update"
:items="items"
:params="params"
:rules="rules"
:api="updateApi"
:args="{module: 'menu'}"
:callback="updateSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.menu-update-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
4. 子菜单列表
心法:子菜单列表页面
武技:开发子菜单列表页面 views/ums/menu/subMenu/SubMenu.vue
<script setup>
import MyNav from "../../../../components/MyNav.vue";
import MyHead from "../../../../components/MyHead.vue";
import MyTable from "../../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../../api/axios";
import {ElMessage} from "element-plus";
import router from "../../../../router/index.js";
// 父菜单ID和父菜单名称
const pid = sessionStorage.getItem('pid');
const parentTitle = sessionStorage.getItem('parentTitle');
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
{icon: 'Menu', label: '菜单列表(子菜单)'}
];
// 数据头
const headItems = [
{type: 'ipt', span: 5, placeholder: '搜索子菜单名', callback: pageByTitle},
];
// 表格列
const columns = [
{label: '菜单图标', prop: 'icon', type: 'icon', width: 90},
{label: '菜单名称', prop: 'title', width: 120},
{label: '路由地址', prop: 'url', width: 120},
{label: '父菜单名称', prop: 'parent.title', width: 120},
{label: '菜单描述', prop: 'info', type: 'card', width: 520},
];
/* ==================== 分页查询 ==================== */
// 表格数据 + 分页数据 + 菜单名称
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let menuTitle = ref();
async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
// 分页参数
let config = {
api: pageApi,
args: {module: 'menu'},
params: {pageNum, pageSize, pid},
records, pageInfo
};
// 若菜单名不为空,则附加为分页条件
if (menuTitle.value) config['params']['title'] = menuTitle.value;
// 发送分页请求
await myPage(config);
}
/* ==================== 搜索菜单名 ==================== */
function pageByTitle(val) {
// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
if (val || menuTitle.value) {
menuTitle.value = val;
page();
}
}
/* ==================== 删除成功回调 ==================== */
function deleteSuccess() {
ElMessage.success('删除成功!');
page();
}
/* ==================== 报表打印 ==================== */
function downloadExcel() {
excelApi('/menu/excel', '菜单报表');
}
/* ==================== 加载函数 ==================== */
onMounted(() => page());
</script>
<template>
<my-nav :items="navItems"/>
<my-head :items="headItems"/>
<my-table module="menu"
insert-page="/SubMenuInsert"
update-page="/SubMenuUpdate"
:records="records"
:columns="columns"
:delete-api="deleteApi"
:delete-batch-api="deleteBatchApi"
:delete-callback="deleteSuccess"
:excel-api="downloadExcel"
:pageInfo="pageInfo"/>
</template>
<style scoped lang="scss"></style>
5. 添加子菜单
心法:添加子菜单页面
武技:开发添加子菜单页面 views/ums/menu//subMenu/SubMenuInsert.vue
<script setup>
import MyForm from "../../../../components/MyForm.vue";
import MyNav from "../../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../../api/axios.js";
import {RULE} from "../../../../const";
import {ElMessage} from "element-plus";
import router from "../../../../router";
// 父菜单ID和父菜单名称
const pid = sessionStorage.getItem('pid');
const parentTitle = sessionStorage.getItem('parentTitle');
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
{icon: 'Menu', label: '菜单列表(子菜单)', url: '/SubMenu'},
{icon: 'Plus', label: '添加新菜单(子菜单)'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '上级菜单', prop: 'parentTitle', disabled: true},
{label: '菜单名称', prop: 'title', required: true, span: 12},
{label: '路由地址', prop: 'url', required: true, span: 12},
{label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
{label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({pid, parentTitle});
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 添加成功后 ==================== */
function insertSuccess() {
ElMessage.success('添加成功!');
setTimeout(() => router.push('/SubMenu'), 1000);
}
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="menu-insert-card" header="添加新菜单(子菜单)">
<my-form type="insert"
:items="items"
:params="params"
:rules="rules"
:api="insertApi"
:args="{module: 'menu'}"
:callback="insertSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.menu-insert-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
6. 修改子菜单
心法:修改子菜单页面
武技:开发修改子菜单页面 views/ums/menu/subMenu/SubMenuUpdate.vue
<script setup>
import MyForm from "../../../../components/MyForm.vue";
import MyNav from "../../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {updateApi} from "../../../../api/axios.js";
import {RULE} from "../../../../const";
import {ElMessage} from "element-plus";
import router from "../../../../router";
// 菜单记录
let menu = JSON.parse(sessionStorage.getItem('row'));
// 父菜单ID和父菜单名称
const pid = sessionStorage.getItem('pid');
const parentTitle = sessionStorage.getItem('parentTitle');
menu['pid'] = pid;
menu['parentTitle'] = parentTitle;
// 路径导航
const navItems = [
{icon: 'Avatar', label: '用户管理'},
{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
{icon: 'Menu', label: '菜单列表(子菜单)', url: '/SubMenu'},
{icon: 'Edit', label: '修改菜单(子菜单)'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
{label: '上级菜单', prop: 'parentTitle', disabled: true},
{label: '菜单名称', prop: 'title', required: true, span: 12},
{label: '路由地址', prop: 'url', required: true, span: 12},
{label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
{label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(menu);
let rules = {title: RULE.TITLE, info: RULE.INFO};
/* ==================== 修改成功后 ==================== */
function updateSuccess() {
ElMessage.success('修改记录成功!');
setTimeout(() => router.push('/SubMenu'), 1000);
}
</script>
<template>
<my-nav :items="navItems"/>
<el-card class="menu-update-card" header="修改菜单(子菜单)">
<my-form type="update"
:items="items"
:params="params"
:rules="rules"
:api="updateApi"
:args="{module: 'menu'}"
:callback="updateSuccess"/>
</el-card>
</template>
<style scoped lang="scss">
.menu-update-card {
width: 60%; // 宽度
margin: 65px auto 0; // 外边距
}
</style>
Java道经 - 项目 - MyClub - 后台前端(四)
传送门:JP3-4-MyClub后台前端(一)
传送门:JP3-4-MyClub后台前端(二)
传送门:JP3-4-MyClub后台前端(三)
传送门:JP3-4-MyClub后台前端(四)
传送门:JP3-4-MyClub后台前端(五)