当前位置: 首页 > ai >正文

JP4-7-MyLesson后台前端(二)

Java道经 - 项目 - MyLesson - 后台前端(二)

文章目录

  • S03. UMS用户服务
    • E01. 用户管理模块
      • 1. 查看用户列表
      • 2. 添加用户记录
      • 3. 修改用户记录
      • 4. 重设用户角色
    • E02. 角色管理模块
      • 1. 查看角色列表
      • 2. 添加角色记录
      • 3. 修改角色记录
      • 4. 重设角色菜单
    • E03. 菜单管理模块
      • 1. 查看父菜单列表
      • 2. 添加父菜单记录
      • 3. 修改父菜单记录
      • 4. 查看子菜单列表
      • 5. 添加子菜单记录
      • 6. 修改子菜单记录

S03. UMS用户服务

武技:在 router/index.js 文件中开发全部相关页面路由配置

import User from "../views/ums/user/User.vue";  
import UserInsert from "../views/ums/user/UserInsert.vue";  
import UserUpdate from "../views/ums/user/UserUpdate.vue";  
import UserUpdateRoles from "../views/ums/user/UserUpdateRoles.vue";  
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";  
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/sub/SubMenu.vue";  
import SubMenuInsert from "../views/ums/menu/sub/SubMenuInsert.vue";  
import SubMenuUpdate from "../views/ums/menu/sub/SubMenuUpdate.vue";const router = createRouter({history: createWebHashHistory(),routes: [{path: '/', name: 'Login', component: Login},{path: '/Main', name: 'Main', component: Main,redirect: '/Dashboard',children: [...{path: '/User', name: 'User', component: User},{path: '/UserInsert', name: 'UserInsert', component: UserInsert},{path: '/UserUpdate', name: 'UserUpdate', component: UserUpdate},{path: '/UserUpdateRoles', name: 'UserUpdateRoles', component: UserUpdateRoles},{path: '/Role', name: 'Role', component: Role},{path: '/RoleInsert', name: 'RoleInsert', component: RoleInsert},{path: '/RoleUpdate', name: 'RoleUpdate', component: RoleUpdate},{path: '/RoleUpdateMenus', name: 'RoleUpdateMenus', component: RoleUpdateMenus},{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},]}]
});

E01. 用户管理模块

1. 查看用户列表

心法:查看用户列表页面

在这里插入图片描述

武技:开发查看用户列表 views/ums/user/User.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, reactive, ref} from "vue";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/index.js";
import {MINIO_AVATAR} from "../../../const/index.js";
import {getResponseData, myPage} from "../../../request/index.js";
import {isNotEmpty, isNotNull} from "../../../util/index.js";
import {genderFormat} from "../../../util/index.js";
import {resetPasswordApi} from "../../../api/ums/user.js";
import {ElMessage, ElMessageBox} from "element-plus";
import router from "../../../router";// 路径导航
const navItems = [{icon: 'User', label: '用户管理'},{icon: 'User', label: '用户列表'},
];
// 数据头
const headItems = [{type: 'ipt', span: 5, placeholder: '按账号搜索', callback: pageByUsername},{type: 'ipt', span: 5, placeholder: '按昵称搜索', callback: pageByNickname},{type: 'ipt', span: 5, placeholder: '按手机搜索', callback: pageByPhone},
]// 表格列
const columns = [{label: '头像', prop: 'avatar', type: 'img', minio: MINIO_AVATAR},{label: '性别', prop: 'gender', type: 'tag', format: genderFormat, width: 80, tagTypeFn:  e => e === 0 ? 'danger' : e === 1 ? 'primary' : 'warning'},{label: '昵称', prop: 'nickname'},{label: '账号', prop: 'username'},{label: '手机', prop: 'phone', width: 100},{label: '邮件', prop: 'email'},{label: '姓名', prop: 'realname', width: 80},{label: '身份证号', prop: 'idcard'},{label: '年龄', prop: 'age', width: 80},{label: '星座', prop: 'zodiac', type: 'tag', width: 80},{label: '籍贯', prop: 'province', type: 'tag', width: 100},{label: '描述', prop: 'info', type: 'card'},
];
// 按钮列
const buttons = [{label: '重设角色', type: 'success', callback: toUserUpdateRoles},{label: '重置密码', type: 'danger', callback: resetPassword}
];/* ==================== 分页查询 ==================== */// 表格数据 + 分页数据
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
// 分页查询条件字段:账号,昵称,手机
let username = ref();
let nickname = ref();
let phone = ref();/*** 分页查询记录** 1. 定义分页基础配置,包括 records, pageInfo, api, params 等。* 2. 附加分页查询条件,如账号,昵称,手机号码等。* 3. 异步发送分页查询请求。** @param pageNum 当前第几页,默认 1* @param pageSize 每页多少条,默认 5*/
async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {// 分页基础配置let config = {api: pageApi,args: {module: 'user'},params: {pageNum, pageSize},records, pageInfo,}// 附加为分页条件if (isNotEmpty(username.value)) config['params']['username'] = username.value;if (isNotEmpty(nickname.value)) config['params']['nickname'] = nickname.value;if (isNotEmpty(phone.value)) config['params']['phone'] = phone.value;// 发送分页请求await myPage(config);
}/* ==================== 按登录账号模糊查询 ==================== *//*** 若输入框有值,或者当前正处于按条件分页状态时,进行操作:** <p> 1. 将输入框中的值赋值给分页条件字段变量。* <p> 2. 重新发送分页请求。** @param val 输入框中的值*/
function pageByUsername(val) {// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求if (isNotNull(val) || username.value) {username.value = val;page();}
}/* ==================== 按用户昵称模糊查询 ==================== *//*** 若输入框有值,或者当前正处于按条件分页状态时,进行操作:** <p> 1. 将输入框中的值赋值给分页条件字段变量。* <p> 2. 重新发送分页请求。** @param val 输入框中的值*/
function pageByNickname(val) {// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求if (isNotNull(val) || nickname.value) {nickname.value = val;page();}
}/* ==================== 按手机号码模糊查询 ==================== *//*** 若输入框有值,或者当前正处于按条件分页状态时,进行操作:** <p> 1. 将输入框中的值赋值给分页条件字段变量。* <p> 2. 重新发送分页请求。** @param val 输入框中的值*/
function pageByPhone(val) {// 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求if (isNotNull(val) || phone.value) {phone.value = val;page();}
}/* ==================== 重置密码 ==================== */async function resetPassword(row) {ElMessageBox.confirm('确认重置密码吗?').then(() => {resetPasswordApi(row['id']).then(res => {if (isNotNull(getResponseData(res))) {ElMessage.success('密码重置为 123456789');}}).catch(() => {ElMessage.info('已取消');});});
}/* ==================== 重设角色 ==================== */function toUserUpdateRoles(row) {sessionStorage.setItem('userId', row['id']);sessionStorage.setItem('nickname', row['nickname'].toString());router.push('/UserUpdateRoles')
}/* ==================== 报表打印 ==================== */function downloadExcel() {excelApi('/user/excel', '用户报表');
}/* ==================== 删除成功回调 ==================== */function deleteSuccess() {ElMessage.success('删除成功');page();
}/* ==================== 加载函数 ==================== */onMounted(() => page());</script><template v-if="records"><my-nav :items="navItems"/><my-head :items="headItems"/><my-table module="user"insert-page="/UserInsert"update-page="/UserUpdate":records="records":columns="columns":buttons="buttons":delete-api="deleteApi":delete-batch-api="deleteBatchApi":delete-callback="deleteSuccess":excel-api="downloadExcel":page-info="pageInfo"/>
</template><style scoped lang="scss"></style>

2. 添加用户记录

心法:添加用户记录页面

在这里插入图片描述

武技:开发添加用户页面 views/ums/user/UserInsert.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyForm from "../../../components/MyForm.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../api/index.js";
import {RULE} from "../../../const/index.js";
import router from "../../../router/index.js";
import {ElMessage} from "element-plus";// 路径导航
const navItems = [{icon: 'User', label: '用户管理'},{icon: 'User', label: '用户列表', url: '/User'},{icon: 'Plus', label: '添加用户'},
];/* ==================== 添加表单 ==================== */// 表单项 + 表单值 + 表单规则
let items = ref([{label: '账号', prop: 'username', required: true, span: 12},{label: '密码', prop: 'password', required: true, type: "password", span: 12},{label: '姓名', prop: 'realname', required: true, span: 12},{label: '邮箱', prop: 'email', required: true, span: 12},{label: '手机号码', prop: 'phone', required: true, span: 12},{label: '身份证号', prop: 'idcard', required: true, span: 12},{label: '描述', prop: 'info', required: true, type: 'textarea'},
]);
let params = reactive({info: '暂无描述'});
let rules = {username: RULE.USERNAME,password: RULE.PASSWORD,realname: RULE.REALNAME,idcard: RULE.IDCARD,phone: RULE.PHONE,email: RULE.EMAIL,info: RULE.INFO
};/* ==================== 添加成功后 ==================== */function insertSuccess() {ElMessage.success('添加记录成功!');setTimeout(() => router.push('/User'), 1000);
}</script><template><my-nav :items="navItems"/><el-card class="user-insert-card" header="添加用户"><my-form type="insert":items="items":params="params":rules="rules":api="insertApi":args="{module: 'user'}":callback="insertSuccess"/></el-card>
</template><style scoped lang="scss">
.user-insert-card {width: 60%; // 宽度margin: 65px auto 0; // 外边距
}
</style>

3. 修改用户记录

心法:修改用户记录页面

在这里插入图片描述

武技:开发修改用户记录页面 views/ums/user/UserUpdate.vue

<script setup>
import router from "../../../router/index.js";
import MyNav from "../../../components/MyNav.vue";
import MyForm from "../../../components/MyForm.vue";
import MyUpload from "../../../components/MyUpload.vue";
import {MINIO_AVATAR, RULE} from "../../../const/index.js";
import {reactive, ref} from "vue";
import {updateApi} from "../../../api/index.js";
import {UPLOAD_AVATAR_URL} from "../../../api/ums/user.js";
import {GENDER_OPTIONS, ZODIAC_OPTIONS} from "../../../const/index.js";
import {ElMessage} from "element-plus";// 获取当前用户记录
let user = JSON.parse(sessionStorage.getItem('row'));
let avatarUrl = ref(MINIO_AVATAR(user['avatar']));// 路径导航
const navItems = [{icon: 'User', label: '用户管理'},{icon: 'User', label: '用户列表', url: '/User'},{icon: 'Edit', label: '修改用户信息'},
];/* ==================== 修改基本信息 ==================== */// 表单项 + 表单值 + 表单规则
let updateFormItems = ref([{label: '账号', prop: 'username', disabled: true, span: 12},{label: '姓名', prop: 'realname', disabled: true, span: 12},{label: '手机号码', prop: 'phone', disabled: true, span: 12},{label: '身份证号', prop: 'idcard', disabled: true, span: 12},{label: '昵称', prop: 'nickname', required: true, span: 12},{label: '邮箱', prop: 'email', required: true, span: 12},{label: '性别', prop: 'gender', required: true, span: 12, type: 'select', options: GENDER_OPTIONS},{label: '年龄', prop: 'age', span: 12, type: 'number', required: true, },{label: '星座', prop: 'zodiac', span: 12, type: 'select', options: ZODIAC_OPTIONS, required: true, },{label: '省份', prop: 'province', span: 12, required: true, },{label: '描述', prop: 'info', type: 'textarea',  required: true, rows: 10},
]);
let updateFormParams= reactive(user);
let updateFormRules = {nickname: RULE.NICKNAME,email: RULE.EMAIL,province: RULE.PROVINCE,info: RULE.INFO,
};/* ==================== 修改成功后 ==================== */function updateSuccess() {ElMessage.success('修改记录成功!');setTimeout(() => router.push('/User'), 1000);
}/* ==================== 上传成功后 ==================== */function uploadAvatarSuccess(data) {avatarUrl.value = MINIO_AVATAR(data);
}
</script><template><my-nav :items="navItems"/><div class="user-update-body"><el-row :gutter="20"><el-col :span="6"><el-card class="user-info-card"><el-image class="avatar-image" :src="avatarUrl"/></el-card><el-card class="upload-avatar-card" header="上传个人头像"><my-upload :url="UPLOAD_AVATAR_URL + '/' + user['id']"name="avatarFile":callback="uploadAvatarSuccess":autoUpload="true"/></el-card></el-col><el-col :span="18"><el-card class="update-card" header="修改基本信息"><my-form type="update":items="updateFormItems":params="updateFormParams":rules="updateFormRules":api="updateApi":args="{module: 'user'}":callback="updateSuccess"/></el-card></el-col></el-row></div>
</template><style scoped lang="scss">
.user-update-body {padding: 0 100px; // 内边距margin: 65px auto 0; // 外边距.user-info-card {text-align: center; // 内容居中.nickname {margin-bottom: 5px; // 下边距}.avatar-image {height: 170px; // 高度}}.upload-avatar-card {margin-top: 25px; // 上外边距}
}
</style>

4. 重设用户角色

心法:重设用户角色页面

在这里插入图片描述

武技:开发重设用户角色页面 views/ums/user/UserUpdateRoles.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import {onMounted, ref} from "vue";
import {ElMessage} from "element-plus";
import {getResponseData} from "../../../request/index.js";
import {listRoleIdsByUserIdApi, updateRolesByUserIdApi} from "../../../api/ums/role.js";
import {isNotNull} from "../../../util/index.js";
import {simpleListApi} from "../../../api/index.js";// 获取当前用户主键和用户昵称
let userId = sessionStorage.getItem('userId');
let nickname = sessionStorage.getItem('nickname');
// 路径导航
const navItems = [{icon: 'User', label: '用户管理'},{icon: 'User', label: '用户列表', url: '/User'},{icon: 'UserFilled', label: '为用户重设角色'},
];
// 全部角色 + 我的角色主键数组
let allRoles = ref([]);
let myRoleIds = ref([]);/* ==================== 确认修改用户的角色 ==================== */async function updateMyRoles() {let data = getResponseData(await updateRolesByUserIdApi(userId, myRoleIds.value));if (isNotNull(data)) {ElMessage.success('角色重设成功,下次登录生效!');}
}/* ==================== 加载函数 ==================== */onMounted(async () => {// 查询全部角色Object.values(getResponseData(await simpleListApi(null, {module: 'role'}))).forEach(role => {allRoles.value.push({label: role['title'], key: role['id']});});// 查询该员工的角色Object.values(getResponseData(await listRoleIdsByUserIdApi(userId))).forEach(roleIds => {myRoleIds.value.push(roleIds);});
});</script><template v-if="allRoles"><div class="user-roles-body"><my-nav :items="navItems"/><el-transfer class="user-roles-transfer"filterable filter-placeholder="输入关键字"v-if="allRoles.length > 0"v-model="myRoleIds":data="allRoles":titles="['全部可选角色', '当前已选角色']":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="updateMyRoles" size="small">确认修改</el-button></template></el-transfer></div>
</template><style scoped lang="scss">
.user-roles-body {text-align: center; // 内容居中.user-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>

E02. 角色管理模块

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/index.js";
import {ElMessage} from "element-plus";
import router from "../../../router/index.js";// 路径导航
const navItems = [{icon: 'User', label: '用户管理'},{icon: 'UserFilled', label: '角色列表'}
];
// 数据头
const headItems = [{type: 'ipt', span: 5, placeholder: '搜索角色标题', callback: pageByTitle},
];
// 表格列
const columns = [{label: '序号', prop: 'idx', type: 'tag'},{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/index.js";
import {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: 'idx', type: 'number', required: true, span: 12},{hidden: true, span: 12},{label: '描述', prop: 'info', required: true, type: 'textarea'},
]);
let params = reactive({});
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/index.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: 'idx', type: 'number', required: true, span: 12},{hidden: true, span: 12},{label: '描述', prop: 'info', required: true, 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 {simpleListApi} from "../../../api/index.js";
import {listMenuIdsByRoleIdApi, updateMenusByRoleIdApi} from "../../../api/ums/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 myMenuIds = 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 updateMenusByRoleIdApi(roleId, buildMenuIds(myMenuIds.value)));if (data) {ElMessage.success('菜单重设成功,下次登录生效!');}
}/* ==================== 加载函数 ==================== */onMounted(async () => {// 查询全部菜单Object.values(getResponseData(await simpleListApi(null, {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 listMenuIdsByRoleIdApi(roleId))).forEach(menuIds => {myMenuIds.value.push(menuIds);});
});</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="myMenuIds":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>

E03. 菜单管理模块

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/index.js";
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: 'idx', type: 'tag'},{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/index.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";// 路径导航
const navItems = [{icon: 'Avatar', label: '用户管理'},{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},{icon: 'Plus', label: '添加新菜单(父菜单)'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([{label: '名称', prop: 'title', required: true, span: 12},{label: '序号', prop: 'idx', type: 'number', required: true, span: 12},{label: '图标', prop: 'icon', required: true, type: 'icon'},{label: '描述', prop: 'info', required: true, 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/index.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: '用户管理'},{icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},{icon: 'Edit', label: '修改菜单(父菜单)'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([{label: '名称', prop: 'title', required: true, span: 12},{label: '序号', prop: 'idx', type: 'number', required: true, span: 12},{label: '图标', prop: 'icon', required: true, type: 'icon'},{label: '描述', prop: 'info', required: true, 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/sub/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/index.js";
import {ElMessage} from "element-plus";// 父菜单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: 'parentMenu.title', width: 90, sortable: false},{label: '序号', prop: 'idx', type: 'tag'},{label: '图标', prop: 'icon', type: 'icon', width: 90},{label: '名称', prop: 'title', width: 120},{label: '地址', prop: 'url', 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/sub/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/index.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: 'title', required: true, span: 12},{label: '序号', prop: 'idx', type: 'number', required: true, span: 12},{label: '地址', prop: 'url', required: true},{label: '图标', prop: 'icon', required: true, type: 'icon'},{label: '描述', prop: 'info', required: true, type: 'textarea', rows: 6},
]);
let params = reactive({pid});
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="`为【${parentTitle}】添加子菜单`"><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/sub/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/index.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: 'idx', type: 'number', required: true, span: 12},{label: '地址', prop: 'url', required: true},{label: '图标', prop: 'icon', required: true, type: 'icon'},{label: '描述', prop: 'info', required: true, type: 'textarea', rows: 5},
]);
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道经 - 项目 - MyLesson - 后台前端(二)

http://www.xdnf.cn/news/20304.html

相关文章:

  • 轻量应用服务器具体指的是什么?
  • Redis《RedisSerializer》
  • 脑电数据预处理十五:小波变换从原理到实践
  • Codeforces Round 1046 (Div. 2) vp补题
  • C++ 详细讲解vector类
  • 检查CDB/PDB 表空间的说明
  • Linux网络接口命名详解:从eth0到ens33
  • [光学原理与应用-431]:非线性光学 - 能生成或改变激光波长的物质或元件有哪些?
  • GPIO的配置中开漏输出与推挽输出的差别
  • C++零基础第四天:顺序、选择与循环结构详解
  • Protobuf
  • 人工智能辅助荧光浓度检测系统:基于YOLO与RGB分析的Python实现
  • 【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
  • AP1272:新一代高性能LDO稳压器,为精密电子系统提供更优电源解决方案
  • 《秦时明月》系列经典语录分享
  • 云原生的12个要素是什么?
  • 【Linux指南】动静态库与链接机制:从原理到实践
  • 疯狂星期四文案网第62天运营日记
  • 消失的6个月!
  • 从文本到知识:使用LLM图转换器构建知识图谱的详细指南
  • Java多线程学习笔记
  • Nginx 实战系列(二)—— Nginx 配置文件与虚拟主机搭建
  • QML Charts组件之LineSeries、SplineSeries与ScatterSeries
  • 正态分布 - 正态分布的经验法则(68-95-99.7 法则)
  • Modbus通信的大端和小端字节序
  • OpsManage 项目启动脚本与 Docker 配置深度分析
  • Day05 单调栈 | 84. 柱状图中最大的矩形、42. 接雨水
  • LeetCode算法日记 - Day 34: 二进制求和、字符串相乘
  • 【目录-多选】鸿蒙HarmonyOS开发者基础
  • 分布式go项目-搭建监控和追踪方案