自存19-48 - 教程
19-菜单管理添加弹窗显示

<script setup> import { ref , reactive , onMounted } from 'vue' import { userGetMenu } from '../../../api' onMounted(() => { //菜单数据 userGetMenu().then(({ data })=>{ console.log(data) permissionData.value = data.data }) }) //form的数据 const form = reactive({ name:'', permissions:'' }) //树形菜单权限数据 const permissionData = ref([]) //弹窗的显示隐藏 const dialogFormVisable = ref(false) //关闭弹窗的回调 const beforeClose = () => { dialogFormVisable.value = false } //选中权限 const defaultKeys = [4,5] const treeRef = ref() </script>

import request from '../utils/request' //发送验证码 export const getCode = (data) => { return request.post('/get/code',data) } //注册用户 export const userAuthentication = (data) =>{ return request.post('/user/authentication',data) } //登录 export const login = (data) =>{ return request.post('/login',data) } //权限管理列表 export const authAdmin = (params) => { return request.get('/auth/admin', { params }) } //菜单权限数据 export const userGetMenu = (params) => { return request.get('/user/getmenu', { params }) }
新添加的部分:

20-菜单管理添加接口联调
<script setup> import { ref, reactive, onMounted } from 'vue' import { userGetMenu, userSetMenu, menuList} from '../../../api' onMounted(() => { //菜单数据 userGetMenu().then(({ data })=>{ console.log(data) permissionData.value = data.data }) getListData() }) const paginationData = reactive({ pageNum: 1, pageSize: 10, }) //请求列表数据 const getListData = () =>{ menuList(paginationData).then(( data ) => { }) } const formRef = ref() //form的数据 const form = reactive({ id:'', name:'', permissions:'' }) //树形菜单权限数据 const permissionData = ref([]) //弹窗的显示隐藏 const dialogFormVisable = ref(false) //关闭弹窗的回调 const beforeClose = () => { dialogFormVisable.value = false } //选中权限 const defaultKeys = [4,5] const treeRef = ref() const rules = reactive({ name:[{ required: true, trigger:'blur',message:'请输入权限名称' }] }) //表单提交 const confirm = async(formEl) => { if (!formEl) return await formEl.validate((valid,fields) => { if(valid){ //获取到选择的checkbox数据 const permissions = JSON.stringify( treeRef.value.getCheckedKeys()) userSetMenu({ name: form.name, permissions, id: form.id}).then(({ data }) => { console.log(data) }) }else{ console.log('error submit!' , fields ) } }) } </script>

import request from '../utils/request' //发送验证码 export const getCode = (data) => { return request.post('/get/code',data) } //注册用户 export const userAuthentication = (data) =>{ return request.post('/user/authentication',data) } //登录 export const login = (data) =>{ return request.post('/login',data) } //权限管理列表 export const authAdmin = (params) => { return request.get('/auth/admin', { params }) } //菜单权限数据 export const userGetMenu = (params) => { return request.get('/user/getmenu', { params }) } //菜单权限修改 export const userSetMenu = (data) => { return request.post('/user/setmenu',data) } //菜单权限列表 export const menuList = (params) => { return request.get('/menu/list', { params }) }

21-菜单管理列表和编辑逻辑

编辑 <script setup> import { ref, reactive, onMounted, nextTick } from 'vue' import { userGetMenu, userSetMenu, menuList} from '../../../api' onMounted(() => { //菜单数据 userGetMenu().then(({ data })=>{ console.log(data) permissionData.value = data.data }) getListData() }) //列表数据 const tableData = reactive({ list: [], total: 0 }) //打开弹窗 const open = (rowData = {}) =>{ dialogFormVisable.value = true // 弹窗打开form生成是异步的 nextTick(() => { if(rowData){ Object.assign(form, { id:rowData.id , name: rowData.name }) treeRef.value.setCheckedKeys(rowData.permission) } }) } const paginationData = reactive({ pageNum: 1, pageSize: 10, }) //请求列表数据 const getListData = () =>{ menuList(paginationData).then(({ data }) => { const { list, total } = data.data tableData.list = list tableData.total = total }) } const formRef = ref() //form的数据 const form = reactive({ id:'', name:'', permissions:'' }) //树形菜单权限数据 const permissionData = ref([]) //弹窗的显示隐藏 const dialogFormVisable = ref(false) //关闭弹窗的回调 const beforeClose = () => { dialogFormVisable.value = false //重置表单 formRef.value.resetFields() //tree选择重置 treeRef.value.setCheckedKeys(defaultKeys) } //选中权限 const defaultKeys = [4,5] const treeRef = ref() const rules = reactive({ name:[{ required: true, trigger:'blur',message:'请输入权限名称' }] }) //表单提交 const confirm = async(formEl) => { if (!formEl) return await formEl.validate((valid,fields) => { if(valid){ //获取到选择的checkbox数据 const permissions = JSON.stringify( treeRef.value.getCheckedKeys()) userSetMenu({ name: form.name, permissions, id: form.id}).then(({ data }) => { console.log(data) }) }else{ console.log('error submit!' , fields ) } }) } </script>
22-菜单管理剩余问题处理

<script setup> import { ref, reactive, onMounted, nextTick } from 'vue' import { userGetMenu, userSetMenu, menuList} from '../../../api' import { Plus } from '@element-plus/icons-vue' onMounted(() => { //菜单数据 userGetMenu().then(({ data })=>{ console.log(data) permissionData.value = data.data }) getListData() }) //列表数据 const tableData = reactive({ list: [], total: 0 }) //打开弹窗 const open = (rowData = {}) =>{ dialogFormVisable.value = true // 弹窗打开form生成是异步的 nextTick(() => { if(rowData){ Object.assign(form, { id:rowData.id , name: rowData.name }) treeRef.value.setCheckedKeys(rowData.permission) } }) } const paginationData = reactive({ pageNum: 1, pageSize: 10, }) const handleSizeChange = (val) => { paginationData.pageSize = val getListData() } const handleCurrentChange = (val) => { paginationData.pageNum = val getListData() } //请求列表数据 const getListData = () =>{ menuList(paginationData).then(({ data }) => { const { list, total } = data.data tableData.list = list tableData.total = total }) } const formRef = ref() //form的数据 const form = reactive({ id:'', name:'', permissions:'' }) //树形菜单权限数据 const permissionData = ref([]) //弹窗的显示隐藏 const dialogFormVisable = ref(false) //关闭弹窗的回调 const beforeClose = () => { dialogFormVisable.value = false //重置表单 formRef.value.resetFields() //tree选择重置 treeRef.value.setCheckedKeys(defaultKeys) } //选中权限 const defaultKeys = [4,5] const treeRef = ref() const rules = reactive({ name:[{ required: true, trigger:'blur',message:'请输入权限名称' }] }) //表单提交 const confirm = async(formEl) => { if (!formEl) return await formEl.validate((valid,fields) => { if(valid){ //获取到选择的checkbox数据 const permissions = JSON.stringify( treeRef.value.getCheckedKeys()) userSetMenu({ name: form.name, permissions, id: form.id}).then(({ data }) => { beforeClose() getListData() }) }else{ console.log('error submit!' , fields ) } }) } </script> 新增 编辑


<script setup> </script>菜单管理菜单规则通常对应一个控制器的方法,同时菜单栏数据也从规则中获取
![]()

import { createApp } from 'vue' import './style.css' import App from './App.vue' import router from './router' import store from './store' import PanelHead from './components/panelHead.vue' router.beforeEach((to,from) =>{ const token = localStorage.getItem('pz_token') //非登录页面token不存在 if (!token && to.path !== '/login') { return '/login' }else if (token && to.path === '/login'){ return '/' }else{ return true } }) // 如果您正在使用CDN引入,请删除下面一行。 import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.component('PanelHead',PanelHead) //路由挂载 app.use(router) //store挂载 app.use(store) app.mount('#app')
23-账号管理列表

![]()


24-账号管理编辑分页功能完成

<script setup> import { authAdmin, menuSelectList, updateUser } from'../../../api' import { ref, reactive, onMounted } from 'vue' import dayjs from 'dayjs' const paginationData = reactive({ pageNum: 1, pageSize: 10, }) //列表数据 const tableData = reactive({ list: [], total: 0 }) onMounted(() => { getListData() menuSelectList().then(({ data }) => { options.value = data.data }) }) //请求列表 const getListData = () => { authAdmin(paginationData).then(({data}) => { console.log(data, 'authAdmin') const { list, total } = data.data list.forEach(item => { item.create_time = dayjs(item.create_time).format('YYYY-MM-DD') }) tableData.list = list tableData.total = total }) } const handleSizeChange = (val) => { paginationData.pageSize = val getListData() } const handleCurrentChange = (val) => { paginationData.pageNum = val getListData() } //弹窗 const dialogFormVisable = ref(false) const beforeClose = () => { } const rules = reactive({ name:[{ required: true, trigger: 'blur' , message:'请填写昵称' }], permissions_id:[{ required: true, trigger: 'blur' , message:'请选择菜单权限' }], }) //编辑表单 const formRef = ref() const form = reactive({ name:'', permissions_id:'', }) //表单提交 const confirm = async (formEl) => { if (!formEl) return await formEl.validate((valid,fields) => { if(valid){ const { name, permissions_id } = form updateUser({ name, permissions_id}).then(({ data }) => { if( data.code === 10000 ){ dialogFormVisable.value = false getListData() } }) }else{ console.log('error submit!' , fields ) } }) } const options = ref([]) //根据权限id匹配权限名称 const permissionName = (id) => { const data = options.value.find(el => el.id === id) return data ? data.name : '超级管理员' } const open = ( rowData ) => { dialogFormVisable.value = true Object.assign(form, { mobile:rowData.mobile, name: rowData.name, permissions_id:rowData.permissions_id }) } </script> {{ permissionName(scope.row.permissions_id) }} {{ scope.row.active ? '正常' : '失效'}} {{ scope.row.create_time}} 编辑


import request from '../utils/request' //发送验证码 export const getCode = (data) => { return request.post('/get/code',data) } //注册用户 export const userAuthentication = (data) =>{ return request.post('/user/authentication',data) } //登录 export const login = (data) =>{ return request.post('/login',data) } //权限管理列表 export const authAdmin = (params) => { return request.get('/auth/admin', { params }) } //菜单权限数据 export const userGetMenu = () => { return request.get('/user/getmenu') } //菜单权限修改 export const userSetMenu = (data) => { return request.post('/user/setmenu',data) } //菜单权限列表 export const menuList = (params) => { return request.get('/menu/list', { params }) } //权限下拉列表 export const menuSelectList = () =>{ return request.get('/menu/selectlist',) } //用户数据修改 export const updateUser = (data) => { return request.post('/update/user',data) }
25-用户权限接口联调和动态路由数据组装



<script setup> import { useStore } from 'vuex' import { computed } from 'vue' import { useRoute,useRouter } from 'vue-router' //拿到store的实例 const store = useStore() // 当前路由对象 const route = useRoute() const router = useRouter() const selectMenu = computed( ()=> store.state.menu.selectMenu) //点击关闭tag const closeTab = (item,index) =>{ store.commit('closeMenu',item) //删除的非当前页tag if(route.path !== item.path){ return } const selectMenuDate = selectMenu.value //删除的最后一项 if( index === selectMenuData.length){ //如果tag只有一个元素 if(!selectMenuData.length) { router.push('/') }else{ router.push({ path:selectMenuData[index -1].path }) } }else(//如果删除的是中间位置tag router.push({ path:selectMenuData[index].path }) ) } const handleClick = (command) => { if(command === "cancel" ){ localStorage.removeItem('pz_token') localStorage.removeItem('pz_userInfo') window.location.href = window.location.origin } } </script>
{{item.name}} admin
退出




<script setup> import { ref, reactive, computed } from 'vue' import { getCode, userAuthentication, login, menuPermissions } from '../../api' import { UserFilled, Lock } from '@element-plus/icons-vue' import { useRouter } from 'vue-router' import { useStore } from 'vuex' const imgUrl = new URL('../../../public/login-head.png', import.meta.url).href //表单数据 const loginForm = reactive({ userName: '', passWord: '', validCode: '', }) //切换表单(0登录 1注册) const formType = ref(0) //点击切换登录和注册 const handleChange = () => { formType.value = formType.value ? 0 : 1 } //账号校验规则 const validateUser = (rule,value,callback) =>{ //不能为空 if(value === ''){ callback(new Error('请输入账号')) }else{ const phoneReg = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/ phoneReg.test(value) ? callback() : callback(Error('手机号格式不对,请输入正确手机号')) } } //密码校验 const validatePass = (rule,value,callback) =>{ //不能为空 if(value === ''){ calllback(new Error('请输入密码')) }else{ const reg = /^[a-zA-Z0-9_-]{4,16}$/ reg.test(value) ? callback() : callback(Error('密码格式不对,需要4-16位字符,请确认格式')) } } //表单校验 const rules = reactive({ userName : [{ validator: validateUser, trigger:'blur'}], passWord : [{ validator: validatePass, trigger:'blur'}] }) //发送短信 const countdown = reactive({ validText:'获取验证码', time:60 }) let flag = false const countdownChange = () =>{ //如果已发送不处理 if(flag) return //判断手机号是否正确 const phoneReg = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/ if (!loginForm.userName || !phoneReg.test(loginForm.userName)){ return ElMessage({ message: '请检查手机号是否正确', type: 'warning', }) } //倒计时 const time = setInterval(()=>{ if( countdown.time <= 0){ countdown.time = 60 countdown.validText = `获取验证码` flag = false clearInterval(time) }else{ countdown.time -= 1 countdown.validText = `剩余${countdown.time}s` } },1000) flag = true getCode({ tel: loginForm.userName}).then(({ data }) => { if(data.code === 10000){ ElMessage.success('发送成功') } }) } const router = useRouter() const loginFromRef = ref() const store = useStore() const routerList = computed(() => store.state.menu.routerList) //表单提交 const submitForm = async (formEl) => { if (!formEl) return //手动触发校验 await formEl.validate((valid, fields) => { if (valid) { //注册页面 if(formType.value){ userAuthentication(loginForm).then(({ data }) => { if (data.code === 10000) { ElMessage.success('注册成功,请登录') formType.value = 0 } }) }else{ //登录页面 login(loginForm).then(({data}) => { if (data.code === 10000) { ElMessage.success('登录成功!') console.log(data) //将token和用户信息缓存到浏览器 localStorage.setItem('pz_token', data.data.token) localStorage.setItem('pz_userInfo', JSON.stringify(data.data.userInfo)) menuPermissions().then(({ data }) => { store.commit('dynamicMenu', data.data) console.log(routerList, 'routerList') // router.push('/') }) } }) } } else { console.log('error submit!', fields) } }) } </script> ![]()
{{ formType ? '返回登录' : '注册账号' }} {{ countdown.validText }} {{ formType ? '注册账号' : '登录'}}


const state = { isCollapse : false, selectMenu: [], routerList: [] } const mutations = { collapseMenu(state){ state.isCollapse = !state.isCollapse }, addMenu(state,payload) { //对数据进行去重 if(state.selectMenu.findIndex(item => item.path === payload.path) === -1){ state.selectMenu.push(payload) } }, closeMenu(state,payload){ //找到点击数据的索引 const index = state.selectMenu.findIndex(val => val.name === payload.name ) //通过索引删除数组指定元素 state.selectMenu.splice(index,1) }, dynamicMenu( state, payload ) { //通过glob导入文件 const modules = import.meta.glob('../views/**/**/*.vue') console.log(modules) function routerSet(router){ router.forEach(route => { //判断没有子菜单,拼接路由数据 if ( !route.children ){ const url = `../views${route.meta.path}/index.vue` //拿到获取的vue组件 route.component = modules[url] }else{ routerSet(route.children) } }) } routerSet(payload) //拿到完整的路由数据 state.routerList = payload } } export default{ state, mutations }
26-动态路由添加和vuex持久化实现











![]()
















浙公网安备 33010602011771号