自存19-48

作者:北慕阳日期:2025/10/2

19-菜单管理添加弹窗显示

1<template>
2   <button @click="dialogFormVisable = true ">打开</button>
3   <el-dialog
4       v-model="dialogFormVisable"
5       :before-close="beforeClose"
6       title="添加权限"
7       width="500"
8   >
9       <el-form
10           ref="formRef"
11           label-width="100px"
12           label-position="left"
13           :model="form"
14       >
15           <el-form-item label="名称" prop="name">
16               <el-input v-model="form.name" placeholder="请填写权限名称"/>
17           </el-form-item>
18            <el-form-item label="权限" prop="permissions">
19               <el-tree
20                   ref="treeRef"
21                   style="max-width: 600px;"
22                   :data="permissionData"
23                   node-key="id"
24                   show-checkbox
25                   :default-checked-keys="defaultKeys"
26               />
27           </el-form-item>
28       </el-form>
29   </el-dialog>
30</template>
31<script setup>
32   import { ref , reactive , onMounted } from 'vue'
33   import { userGetMenu } from '../../../api'
34   
35   onMounted(() => {
36       //菜单数据
37       userGetMenu().then(({ data })=>{
38           console.log(data)
39           permissionData.value = data.data
40       })
41   })
42   //form的数据
43   const form = reactive({
44       name:'',
45       permissions:''
46   })
47   //树形菜单权限数据
48   const permissionData = ref([])
49   //弹窗的显示隐藏
50   const dialogFormVisable = ref(false)
51   //关闭弹窗的回调
52   const beforeClose = () => {
53       dialogFormVisable.value = false
54   }
55   //选中权限
56   const defaultKeys = [4,5]
57   const treeRef = ref()
58</script>
59<style lang="less" scoped>
60</style>

1import request from '../utils/request'
2//发送验证码
3export const getCode = (data) => {
4   return request.post('/get/code',data)
5}
6
7//注册用户
8export const userAuthentication = (data) =>{
9   return request.post('/user/authentication',data)
10}
11
12//登录
13export const login = (data) =>{
14   return request.post('/login',data)
15} 
16
17//权限管理列表
18export const authAdmin = (params) => {
19   return request.get('/auth/admin', { params })
20}
21
22//菜单权限数据
23export const userGetMenu = (params) => {
24   return request.get('/user/getmenu', { params })
25}

新添加的部分:

20-菜单管理添加接口联调

1<template>
2   <button @click="dialogFormVisable = true ">打开</button>
3   <el-dialog
4       v-model="dialogFormVisable"
5       :before-close="beforeClose"
6       title="添加权限"
7       width="500"
8   >
9       <el-form
10           ref="formRef"
11           label-width="100px"
12           label-position="left"
13           :model="form"
14           :rules="rules"
15       >
16            <el-form-item v-show="false" prop="id">
17               <el-input v-model="form.id"/>
18            </el-form-item>
19           <el-form-item label="名称" prop="name">
20               <el-input v-model="form.name" placeholder="请填写权限名称"/>
21           </el-form-item>
22            <el-form-item label="权限" prop="permissions">
23               <el-tree
24                   ref="treeRef"
25                   style="max-width: 600px;"
26                   :data="permissionData"
27                   node-key="id"
28                   show-checkbox
29                   :default-checked-keys="defaultKeys"
30                   :default-expanded-keys="[2]"
31               />
32           </el-form-item>
33       </el-form>
34       <template #footer>
35           <div class="dialog-footer">
36               <el-button type="primary"  @click="confirm(formRef)">确认</el-button>
37           </div>
38       </template>
39   </el-dialog>
40</template>
41<script setup>
42   import { ref, reactive, onMounted } from 'vue'
43   import { userGetMenu, userSetMenu, menuList} from '../../../api'
44
45
46   onMounted(() => {
47       //菜单数据
48       userGetMenu().then(({ data })=>{
49           console.log(data)
50           permissionData.value = data.data
51       })
52       getListData()
53   })
54   const paginationData = reactive({
55       pageNum: 1,
56       pageSize: 10,
57   })
58
59   //请求列表数据
60   const getListData = () =>{
61       menuList(paginationData).then(( data ) => {
62
63       })
64   }
65
66   const formRef = ref()
67
68   //form的数据
69   const form = reactive({
70       id:'',
71       name:'',
72       permissions:''
73   })
74   //树形菜单权限数据
75   const permissionData = ref([])
76   //弹窗的显示隐藏
77   const dialogFormVisable = ref(false)
78   //关闭弹窗的回调
79   const beforeClose = () => {
80       dialogFormVisable.value = false
81   }
82   //选中权限
83   const defaultKeys = [4,5]
84   const treeRef = ref()
85
86   const rules = reactive({
87       name:[{ required: true, trigger:'blur',message:'请输入权限名称' }]
88   })
89   //表单提交
90   const confirm = async(formEl) => {
91       if (!formEl) return
92       await formEl.validate((valid,fields) => {
93           if(valid){
94               //获取到选择的checkbox数据
95               const permissions = JSON.stringify( treeRef.value.getCheckedKeys())
96               userSetMenu({ name: form.name, permissions, id: form.id}).then(({ data }) => {
97                   console.log(data)
98               })
99           }else{
100               console.log('error submit!' , fields )
101           }
102       })
103   }
104</script>
105<style lang="less" scoped>
106</style>

1import request from '../utils/request'
2//发送验证码
3export const getCode = (data) => {
4   return request.post('/get/code',data)
5}
6
7//注册用户
8export const userAuthentication = (data) =>{
9   return request.post('/user/authentication',data)
10}
11
12//登录
13export const login = (data) =>{
14   return request.post('/login',data)
15} 
16
17//权限管理列表
18export const authAdmin = (params) => {
19   return request.get('/auth/admin', { params })
20}
21
22//菜单权限数据
23export const userGetMenu = (params) => {
24   return request.get('/user/getmenu', { params })
25}
26
27//菜单权限修改
28export const userSetMenu = (data) => {
29    return request.post('/user/setmenu',data)
30}
31
32//菜单权限列表
33export const menuList = (params) => {
34   return request.get('/menu/list', { params })
35}

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

1<template>
2   <button @click="open(null)">打开 </button>
3   <el-table :data="tableData.list" style="width: 100%">
4       <el-table-column prop="id" label="id" />
5       <el-table-column prop="name" label="昵称" />
6       <el-table-column prop="permissionName" label="菜单权限" width="500px"/>
7       <el-table-column label="操作">
8           <template #default="scope">
9              <el-button type="primary" @click="open(scope.row)">编辑</el-button>
10           </template>
11       </el-table-column>
12   </el-table>
13   <el-dialog
14       v-model="dialogFormVisable"
15       :before-close="beforeClose"
16       title="添加权限"
17       width="500"
18   >
19       <el-form
20           ref="formRef"
21           label-width="100px"
22           label-position="left"
23           :model="form"
24           :rules="rules"
25       >
26            <el-form-item v-show="false" prop="id">
27               <el-input v-model="form.id"/>
28            </el-form-item>
29           <el-form-item label="名称" prop="name">
30               <el-input v-model="form.name" placeholder="请填写权限名称"/>
31           </el-form-item>
32            <el-form-item label="权限" prop="permissions">
33               <el-tree
34                   ref="treeRef"
35                   style="max-width: 600px;"
36                   :data="permissionData"
37                   node-key="id"
38                   show-checkbox
39                   :default-checked-keys="defaultKeys"
40                   :default-expanded-keys="[2]"
41               />
42           </el-form-item>
43       </el-form>
44       <template #footer>
45           <div class="dialog-footer">
46               <el-button type="primary"  @click="confirm(formRef)">确认</el-button>
47           </div>
48       </template>
49   </el-dialog>
50</template>
51<script setup>
52   import { ref, reactive, onMounted, nextTick } from 'vue'
53   import { userGetMenu, userSetMenu, menuList} from '../../../api'
54
55
56   onMounted(() => {
57       //菜单数据
58       userGetMenu().then(({ data })=>{
59           console.log(data)
60           permissionData.value = data.data
61       })
62       getListData()
63   })
64
65   //列表数据
66   const tableData = reactive({
67       list: [],
68       total: 0
69   })
70   //打开弹窗
71   const open = (rowData = {}) =>{
72       dialogFormVisable.value = true
73       // 弹窗打开form生成是异步的
74       nextTick(() => {
75           if(rowData){
76               Object.assign(form, { id:rowData.id , name: rowData.name  })
77               treeRef.value.setCheckedKeys(rowData.permission)
78           }
79       })
80   }
81
82   const paginationData = reactive({
83       pageNum: 1,
84       pageSize: 10,
85   })
86
87   //请求列表数据
88   const getListData = () =>{
89       menuList(paginationData).then(({ data }) => {
90           const { list, total } = data.data
91           tableData.list = list
92           tableData.total = total
93       })
94   }
95
96   const formRef = ref()
97
98   //form的数据
99   const form = reactive({
100       id:'',
101       name:'',
102       permissions:''
103   })
104   //树形菜单权限数据
105   const permissionData = ref([])
106   //弹窗的显示隐藏
107   const dialogFormVisable = ref(false)
108   //关闭弹窗的回调
109   const beforeClose = () => {
110       dialogFormVisable.value = false
111       //重置表单
112       formRef.value.resetFields()
113       //tree选择重置
114       treeRef.value.setCheckedKeys(defaultKeys)
115   }
116   //选中权限
117   const defaultKeys = [4,5]
118   const treeRef = ref()
119
120   const rules = reactive({
121       name:[{ required: true, trigger:'blur',message:'请输入权限名称' }]
122   })
123   //表单提交
124   const confirm = async(formEl) => {
125       if (!formEl) return
126       await formEl.validate((valid,fields) => {
127           if(valid){
128               //获取到选择的checkbox数据
129               const permissions = JSON.stringify( treeRef.value.getCheckedKeys())
130               userSetMenu({ name: form.name, permissions, id: form.id}).then(({ data }) => {
131                   console.log(data)
132               })
133           }else{
134               console.log('error submit!' , fields )
135           }
136       })
137   }
138</script>
139<style lang="less" scoped>
140</style>

22-菜单管理剩余问题处理

1<template>
2   <panel-head/>
3   <div class="btns">
4       <el-button  :icon="Plus" type="primary" @click="open(null)" size="small">新增</el-button>
5   </div>
6   <el-table :data="tableData.list" style="width: 100%">
7       <el-table-column prop="id" label="id" />
8       <el-table-column prop="name" label="昵称" />
9       <el-table-column prop="permissionName" label="菜单权限" width="500px"/>
10       <el-table-column label="操作">
11           <template #default="scope">
12              <el-button type="primary" @click="open(scope.row)">编辑</el-button>
13           </template>
14       </el-table-column>
15   </el-table>
16   <div class="pagination-info">
17       <el-pagination
18           v-model:current-page="paginationData.pageNum"
19           :page-size="paginationData.pageSize"
20           :background="false"
21           size="small"
22           layout="total, prev, pager, next"
23           :total="tableData.total"
24           @size-change="handleSizeChange"
25           @current-change="handleCurrentChange"
26           />
27   </div>
28   <el-dialog
29       v-model="dialogFormVisable"
30       :before-close="beforeClose"
31       title="添加权限"
32       width="500"
33   >
34       <el-form
35           ref="formRef"
36           label-width="100px"
37           label-position="left"
38           :model="form"
39           :rules="rules"
40       >
41            <el-form-item v-show="false" prop="id">
42               <el-input v-model="form.id"/>
43            </el-form-item>
44           <el-form-item label="名称" prop="name">
45               <el-input v-model="form.name" placeholder="请填写权限名称"/>
46           </el-form-item>
47            <el-form-item label="权限" prop="permissions">
48               <el-tree
49                   ref="treeRef"
50                   style="max-width: 600px;"
51                   :data="permissionData"
52                   node-key="id"
53                   show-checkbox
54                   :default-checked-keys="defaultKeys"
55                   :default-expanded-keys="[2]"
56               />
57           </el-form-item>
58       </el-form>
59       <template #footer>
60           <div class="dialog-footer">
61               <el-button type="primary"  @click="confirm(formRef)">确认</el-button>
62           </div>
63       </template>
64   </el-dialog>
65</template>
66<script setup>
67   import { ref, reactive, onMounted, nextTick } from 'vue'
68   import { userGetMenu, userSetMenu, menuList} from '../../../api'
69   import { Plus } from '@element-plus/icons-vue'
70
71
72   onMounted(() => {
73       //菜单数据
74       userGetMenu().then(({ data })=>{
75           console.log(data)
76           permissionData.value = data.data
77       })
78       getListData()
79   })
80
81   //列表数据
82   const tableData = reactive({
83       list: [],
84       total: 0
85   })
86   //打开弹窗
87   const open = (rowData = {}) =>{
88       dialogFormVisable.value = true
89       // 弹窗打开form生成是异步的
90       nextTick(() => {
91           if(rowData){
92               Object.assign(form, { id:rowData.id , name: rowData.name  })
93               treeRef.value.setCheckedKeys(rowData.permission)
94           }
95       })
96   }
97
98   const paginationData = reactive({
99       pageNum: 1,
100       pageSize: 10,
101   })
102
103   const handleSizeChange = (val) => {
104       paginationData.pageSize = val
105       getListData()
106   }
107   const handleCurrentChange = (val) => {
108       paginationData.pageNum = val
109       getListData()
110   }
111   //请求列表数据
112   const getListData = () =>{
113       menuList(paginationData).then(({ data }) => {
114           const { list, total } = data.data
115           tableData.list = list
116           tableData.total = total
117       })
118   }
119
120   const formRef = ref()
121
122   //form的数据
123   const form = reactive({
124       id:'',
125       name:'',
126       permissions:''
127   })
128   //树形菜单权限数据
129   const permissionData = ref([])
130   //弹窗的显示隐藏
131   const dialogFormVisable = ref(false)
132   //关闭弹窗的回调
133   const beforeClose = () => {
134       dialogFormVisable.value = false
135       //重置表单
136       formRef.value.resetFields()
137       //tree选择重置
138       treeRef.value.setCheckedKeys(defaultKeys)
139   }
140   //选中权限
141   const defaultKeys = [4,5]
142   const treeRef = ref()
143
144   const rules = reactive({
145       name:[{ required: true, trigger:'blur',message:'请输入权限名称' }]
146   })
147   //表单提交
148   const confirm = async(formEl) => {
149       if (!formEl) return
150       await formEl.validate((valid,fields) => {
151           if(valid){
152               //获取到选择的checkbox数据
153               const permissions = JSON.stringify( treeRef.value.getCheckedKeys())
154               userSetMenu({ name: form.name, permissions, id: form.id}).then(({ data }) => {
155                   beforeClose()
156                   getListData()
157               })
158           }else{
159               console.log('error submit!' , fields )
160           }
161       })
162   }
163</script>
164<style lang="less" scoped>
165.btns{
166   padding: 10px 0 10px 10px ;
167   background-color: #fff;
168}
169</style>

1<template>
2   <div class="panel-heading">
3       <div class="panel-lead">
4           <div class="title">菜单管理</div>
5           <p class="description">菜单规则通常对应一个控制器的方法,同时菜单栏数据也从规则中获取</p>
6       </div>
7   </div>
8</template>
9
10<script setup>
11</script>
12
13<style lang="less" scoped>
14.panel-heading {
15   padding: 15px;
16   background: #e8edf0;
17   border-color: #e8edf0;
18   position: relative;
19   .panel-lead {
20     font-size: 14px;
21     .title {
22       font-weight: bold;
23       font-style: normal;
24     }
25     .description {
26       margin-top: 5px;
27     }
28   }
29 }
30</style>

1import { createApp } from 'vue'
2import './style.css'
3import App from './App.vue'
4import router from './router'
5import store from './store'
6import PanelHead from './components/panelHead.vue'
7
8
9router.beforeEach((to,from) =>{
10 const token = localStorage.getItem('pz_token')
11 //非登录页面token不存在
12 if (!token && to.path !== '/login') {
13   return '/login'
14 }else if (token && to.path === '/login'){
15   return '/'
16 }else{
17   return true
18 }
19})
20
21// 如果您正在使用CDN引入,请删除下面一行。
22import * as ElementPlusIconsVue from '@element-plus/icons-vue'
23
24const app = createApp(App)
25for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
26 app.component(key, component)
27}
28
29app.component('PanelHead',PanelHead)
30//路由挂载
31app.use(router)
32//store挂载
33app.use(store)
34app.mount('#app')
35

23-账号管理列表

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

1<template>
2   <panel-head/>
3   <el-table :data="tableData.list" style="width: 100%">
4       <el-table-column prop="id" label="id" />
5       <el-table-column prop="name" label="昵称" />
6       <el-table-column prop="permissions_id" label="所属组别" >
7           <template #default="scope">
8              {{ permissionName(scope.row.permissions_id) }}
9           </template>
10       </el-table-column>
11        <el-table-column prop="mobile" label="手机号" />
12
13        <el-table-column prop="active" label="状态" >
14            <template #default="scope">
15               <el-tag  :type="scope.row.active ? 'success' : 'danger' ">{{ scope.row.active ? '正常' : '失效'}}</el-tag>
16           </template>
17       </el-table-column>
18
19       <el-table-column  label="创建时间" >
20            <template #default="scope">
21               <div class="flex-box">
22                   <el-icon><Clock /></el-icon>
23                   <span style="margin-left : 10px">{{ scope.row.create_time}}</span>
24               </div>
25           </template>
26       </el-table-column>
27
28       <el-table-column label="操作">
29           <template #default="scope">
30              <el-button type="primary" @click="open(scope.row)">编辑</el-button>
31           </template>
32       </el-table-column>
33   </el-table>
34
35   <div class="pagination-info">
36       <el-pagination
37           v-model:current-page="paginationData.pageNum"
38           :page-size="paginationData.pageSize"
39           :background="false"
40           size="small"
41           layout="total, prev, pager, next"
42           :total="tableData.total"
43           @size-change="handleSizeChange"
44           @current-change="handleCurrentChange"
45           />
46   </div>
47   <el-dialog
48       v-model="dialogFormVisable"
49       :before-close="beforeClose"
50       title="添加权限"
51       width="500"
52   >
53       <el-form
54               ref="formRef"
55               label-width="100px"
56               label-position="left"
57               :model="form"
58               :rules="rules"
59           >
60               <el-form-item label="手机号" prop="mobile">
61                   <el-input v-model="form.mobile" disabled/>
62               </el-form-item>
63               <el-form-item label="昵称" prop="name">
64                   <el-input v-model="form.name" />
65               </el-form-item>
66               <el-form-item label="菜单权限" prop="permissions_id">
67                   <el-select
68                       v-model="form.permissions_id"
69                       placeholder="请选择菜单权限"
70                       style="width: 240px"
71                   >
72                       <el-option
73                           v-for="item in options" 
74                           :key="item.id"
75                           :label="item.name"
76                           :value="item.id"       
77                           />
78                   </el-select>
79               </el-form-item>
80       </el-form>
81       <template #footer>
82           <div class="dialog-footer">
83               <el-button type="primary"  @click="confirm(formRef)">确认</el-button>
84           </div>
85       </template>
86   </el-dialog>
87</template>
88
89<script setup>
90import { authAdmin, menuSelectList, updateUser } from'../../../api'
91import { ref, reactive, onMounted } from 'vue'
92import dayjs from 'dayjs'
93
94const paginationData = reactive({
95   pageNum: 1,
96   pageSize: 10,
97})
98
99//列表数据
100const tableData = reactive({
101   list: [],
102   total: 0
103})
104
105onMounted(() => {
106   getListData()
107   menuSelectList().then(({ data }) => {
108       options.value = data.data
109   })
110})
111//请求列表
112const getListData = () => {
113   authAdmin(paginationData).then(({data}) => {
114       console.log(data, 'authAdmin')
115       const { list, total } = data.data
116       list.forEach(item => {
117           item.create_time = dayjs(item.create_time).format('YYYY-MM-DD')
118       })
119       tableData.list = list
120       tableData.total = total
121   })
122}
123
124const handleSizeChange = (val) => {
125   paginationData.pageSize = val
126   getListData()
127}
128const handleCurrentChange = (val) => {
129   paginationData.pageNum = val
130   getListData()
131}
132
133//弹窗
134const dialogFormVisable = ref(false)
135const beforeClose = () => {
136
137}
138
139const rules = reactive({
140   name:[{ required: true, trigger: 'blur' , message:'请填写昵称' }],
141   permissions_id:[{ required: true, trigger: 'blur' , message:'请选择菜单权限' }],
142
143})
144
145//编辑表单
146const formRef = ref()
147const form = reactive({
148   name:'',
149   permissions_id:'',
150
151})
152//表单提交
153const confirm = async (formEl) => {
154   if (!formEl) return
155   await formEl.validate((valid,fields) => {
156       if(valid){
157           const { name, permissions_id } = form
158           updateUser({ name, permissions_id}).then(({ data }) => {
159               if( data.code === 10000 ){
160                   dialogFormVisable.value = false
161                   getListData()
162               }
163           })
164       }else{
165           console.log('error submit!' , fields )
166       }
167   })
168}
169
170const options = ref([])
171//根据权限id匹配权限名称
172const permissionName = (id) => {
173   const data = options.value.find(el => el.id === id)
174   return data ? data.name : '超级管理员'
175}
176const open = ( rowData ) => {
177   dialogFormVisable.value = true
178   Object.assign(form, { mobile:rowData.mobile, name: rowData.name, permissions_id:rowData.permissions_id })
179}
180</script>
181
182<style lang="less" scoped>
183.flex-box{
184   display: flex;
185   align-items: center;
186}
187</style>

1import request from '../utils/request'
2//发送验证码
3export const getCode = (data) => {
4   return request.post('/get/code',data)
5}
6
7//注册用户
8export const userAuthentication = (data) =>{
9   return request.post('/user/authentication',data)
10}
11
12//登录
13export const login = (data) =>{
14   return request.post('/login',data)
15} 
16
17//权限管理列表
18export const authAdmin = (params) => {
19   return request.get('/auth/admin', { params })
20}
21
22//菜单权限数据
23export const userGetMenu = () => {
24   return request.get('/user/getmenu')
25}
26
27//菜单权限修改
28export const userSetMenu = (data) => {
29    return request.post('/user/setmenu',data)
30}
31
32//菜单权限列表
33export const menuList = (params) => {
34   return request.get('/menu/list', { params })
35}
36
37//权限下拉列表
38export const menuSelectList = () =>{
39   return request.get('/menu/selectlist',)
40}
41
42//用户数据修改
43export const updateUser = (data) => {
44    return request.post('/update/user',data)
45}
46

25-用户权限接口联调和动态路由数据组装

1<template>
2   <div class="header-container">
3       <div class="header-left flex-box" >
4           <el-icon class="icon" size="20" @click="store.commit('collapseMenu')"><Fold /></el-icon>
5           <ul class="flex-box">
6               <li 
7                   v-for="(item,index) in selectMenu" 
8                   :key="item.path"
9                   :class="{selected: route.path === item.path}"
10                   class="tab flex-box">
11                   <el-icon  size="12" ><component :is="item.icon" /></el-icon>
12                   <router-link class="text flex-box" :to="{ path:item.path }">
13                       {{item.name}}
14                   </router-link>
15                   <el-icon  size="12" class="close" @click="closeTab(item,index)"><Close /></el-icon>
16               </li>
17           </ul>
18       </div>
19       <div class="header-right">
20           <el-dropdown @command="handleClick">
21              <div class="el-dropdown-link flex-box">
22                   <el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"/>
23                   <p class="user-name">admin</p>
24              </div>
25               <template #dropdown>
26                   <el-dropdown-item command="cancel">退出</el-dropdown-item>
27               </template>
28           </el-dropdown>
29       </div>
30   </div>
31</template>
32
33<script setup>
34import { useStore } from 'vuex'
35import { computed } from 'vue'
36import { useRoute,useRouter } from 'vue-router'
37//拿到store的实例
38const store = useStore()
39// 当前路由对象
40const route = useRoute()
41const router = useRouter()
42const selectMenu = computed( ()=> store.state.menu.selectMenu)
43//点击关闭tag
44const closeTab = (item,index) =>{
45   store.commit('closeMenu',item)
46   //删除的非当前页tag
47   if(route.path !== item.path){
48       return
49   }
50   const selectMenuDate = selectMenu.value
51   //删除的最后一项
52   if( index === selectMenuData.length){
53       //如果tag只有一个元素
54       if(!selectMenuData.length) {
55           router.push('/')
56       }else{
57           router.push({
58               path:selectMenuData[index -1].path
59           })
60       }
61   }else(//如果删除的是中间位置tag
62       router.push({
63           path:selectMenuData[index].path
64       })
65   )
66}
67
68const handleClick = (command) => {
69   if(command === "cancel" ){
70       localStorage.removeItem('pz_token')
71       localStorage.removeItem('pz_userInfo')
72
73       window.location.href = window.location.origin
74   }
75}
76</script>
77
78<style lang="less" scoped>
79.flex-box{
80   display: flex;
81   align-items: center;
82   height: 100%;
83}
84.header-container {
85   display: flex;
86   justify-content: space-between;
87   align-items: center;
88   height: 100%;
89   background-color: #fff;
90   padding-right: 25px;
91   .header-left{
92       height: 100%;
93       .icon{
94           width: 45px;
95           height: 100%;
96       }
97       .icon:hover{
98           background-color: #f5f5f5;
99           cursor: pointer;
100       }
101       .tab{
102           padding: 0 10px;
103           height: 100%;
104           .text{
105               margin: 0 5px;
106           }
107           .close{
108               visibility: hidden;
109           }
110           &.selected{
111               a{
112                   color: #409eff;
113               }
114               i{
115                   color: #409eff;
116               }
117               background-color: #f5f5f5;
118           }
119       }
120       .tab:hover{
121           background-color: #f5f5f5;
122           .close{
123               visibility: inherit;
124               cursor: pointer;
125               color: #000;
126           }
127       }
128   }
129   .header-right{
130       .user-name{
131           margin-left: 10px;
132       }
133   }
134   a{ 
135       height: 100%;
136       color: #333;
137       font-size: 15px;
138   }
139}
140</style>

1<template>
2       <el-row class="login-container" justify="center" :align="'middle'">
3           <el-card style="max-width: 480px">
4               <template #header>
5                   <div class="card-header">
6                       <img :src="imgUrl" alt="">
7                   </div>
8               </template>
9               <div class="jump-link">
10                   <el-link type="primary" @click="handleChange">{{ formType ? '返回登录' : '注册账号' }}</el-link>
11               </div>
12                <el-form 
13                ref="loginFromRef"
14                :model="loginForm" 
15                style="max-width: 600px"
16                class="demo-ruleForm"
17                :rules="rules"
18                >
19                   <el-form-item prop="userName">
20                       <el-input v-model="loginForm.userName" :prefix-icon="UserFilled" placeholder="手机号"></el-input>
21                   </el-form-item>
22                   <el-form-item prop="passWord">
23                       <el-input v-model="loginForm.passWord" type="password" :prefix-icon="Lock" placeholder="密码"></el-input>
24                   </el-form-item>
25                   <el-form-item v-if="formType" prop="validCode">
26                       <el-input v-model="loginForm.validCode" :prefix-icon="Lock" placeholder="验证码">
27                           <template #append>
28                               <span @click="countdownChange">{{ countdown.validText }}</span>
29                           </template>
30                       </el-input>
31                   </el-form-item>
32                   <el-form-item>
33                       <el-button type="primary" :style="{width:'100%'}" @click="submitForm(loginFromRef)">
34                           {{ formType ? '注册账号' : '登录'}}
35                       </el-button>
36                   </el-form-item>
37                </el-form>
38           </el-card>
39       </el-row>
40</template>
41
42<script setup>
43import { ref, reactive, computed } from 'vue'
44import { getCode, userAuthentication, login, menuPermissions  } from '../../api'
45import { UserFilled, Lock } from '@element-plus/icons-vue'
46import { useRouter } from 'vue-router' 
47import { useStore } from 'vuex'
48
49const imgUrl = new URL('../../../public/login-head.png', import.meta.url).href
50
51//表单数据
52const loginForm = reactive({
53   userName: '',
54   passWord: '',
55   validCode: '',
56})
57//切换表单(0登录 1注册)
58const formType = ref(0)
59//点击切换登录和注册
60const handleChange = () => {
61   formType.value = formType.value ? 0 : 1
62}
63//账号校验规则
64const validateUser = (rule,value,callback) =>{
65   //不能为空
66   if(value === ''){
67       callback(new Error('请输入账号'))
68   }else{
69       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}$/
70       phoneReg.test(value) ? callback() : callback(Error('手机号格式不对,请输入正确手机号'))
71   }
72}
73//密码校验
74const validatePass = (rule,value,callback) =>{
75   //不能为空
76   if(value === ''){
77       calllback(new Error('请输入密码'))
78   }else{
79       const reg = /^[a-zA-Z0-9_-]{4,16}$/
80       reg.test(value) ? callback() : callback(Error('密码格式不对,需要4-16位字符,请确认格式'))
81   }
82}
83//表单校验
84const rules = reactive({
85   userName : [{ validator: validateUser, trigger:'blur'}],
86   passWord : [{ validator: validatePass, trigger:'blur'}]
87})
88//发送短信
89const countdown = reactive({
90   validText:'获取验证码',
91   time:60
92})
93let flag = false
94const countdownChange = () =>{
95   //如果已发送不处理
96   if(flag) return
97   //判断手机号是否正确
98   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}$/
99   if (!loginForm.userName || !phoneReg.test(loginForm.userName)){
100       return  ElMessage({
101               message: '请检查手机号是否正确',
102               type: 'warning',
103       })
104   }
105   //倒计时
106   const time = setInterval(()=>{
107       if( countdown.time <= 0){
108           countdown.time = 60
109           countdown.validText = `获取验证码`
110           flag = false
111           clearInterval(time)
112       }else{
113           countdown.time -= 1
114           countdown.validText = [`剩余${countdown.time}s`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.time.md)
115       }
116   },1000)
117   flag = true
118   getCode({ tel: loginForm.userName}).then(({ data }) => {
119       if(data.code === 10000){
120           ElMessage.success('发送成功')
121       }
122   })
123}
124
125const router = useRouter()
126const loginFromRef = ref()
127const store = useStore()
128
129const routerList =  computed(() => store.state.menu.routerList)
130//表单提交
131const submitForm = async (formEl) => { 
132   if (!formEl) return
133   //手动触发校验
134   await formEl.validate((valid, fields) => {
135       if (valid) {
136           //注册页面
137           if(formType.value){
138               userAuthentication(loginForm).then(({ data }) => {
139                   if (data.code === 10000) {
140                       ElMessage.success('注册成功,请登录')
141                       formType.value = 0
142                   }
143               })
144           }else{
145               //登录页面
146               login(loginForm).then(({data}) => {
147                    if (data.code === 10000) {
148                       ElMessage.success('登录成功!')
149                       console.log(data)
150                       //将token和用户信息缓存到浏览器
151                       localStorage.setItem('pz_token', data.data.token)
152                       localStorage.setItem('pz_userInfo', JSON.stringify(data.data.userInfo))
153                       menuPermissions().then(({ data }) => {
154                           store.commit('dynamicMenu', data.data)
155                           console.log(routerList, 'routerList')
156                           // router.push('/')
157                       })
158                       
159                   }
160               })
161           }
162       } else {
163       console.log('error submit!', fields)
164       }
165   })
166}
167</script>
168
169<style lang="less" scoped>
170:deep(.el-card__header) {
171   padding: 0
172}
173 .login-container {
174   height: 100%;
175   .card-header{
176     background-color: #899fe1;
177     img {
178       width: 430px;
179     }
180   }
181   .jump-link {
182     text-align: right;
183     margin-bottom: 10px;
184   }
185 }
186</style>

1const state = {
2   isCollapse : false,
3   selectMenu: [],
4   routerList: []
5}
6const mutations = {
7   collapseMenu(state){
8       state.isCollapse = !state.isCollapse
9   },
10   addMenu(state,payload) {
11       //对数据进行去重
12       if(state.selectMenu.findIndex(item => item.path === payload.path) === -1){
13           state.selectMenu.push(payload)
14
15       }
16   },
17   closeMenu(state,payload){
18       //找到点击数据的索引
19       const index = state.selectMenu.findIndex(val => val.name === payload.name )
20       //通过索引删除数组指定元素
21       state.selectMenu.splice(index,1)
22   },
23   dynamicMenu( state, payload ) {
24       //通过glob导入文件
25       const modules =  import.meta.glob('../views/**/**/*.vue')
26        console.log(modules)
27        function routerSet(router){
28           router.forEach(route => {
29               //判断没有子菜单,拼接路由数据
30               if ( !route.children ){
31                   const url = [`../views${route.meta.path}/index.vue`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.meta.md)
32                   //拿到获取的vue组件
33                   route.component =  modules[url]
34               }else{
35                   routerSet(route.children)
36               }
37           })
38        }
39        routerSet(payload)
40        //拿到完整的路由数据
41        state.routerList =  payload
42   }
43
44}
45
46export default{
47   state,
48   mutations
49}

26-动态路由添加和vuex持久化实现

27-菜单高亮显示和用户信息显示问题解决

28-陪护管理新增陪护师弹窗

29-陪护新增表单校验和接口联调

30-陪护列表显示和接口联调

31-陪护师编辑和批量删除接口联调

32-【C端】项目创建引入router和vant UI

33-【C端】tabbar引入和layout组件实现

34-【C端】登录页面显示效果实现

35-【C端】axios二次封装和登录接口联调

36-【C端】首页页面效果实现

37-【C端】订单详情自定义头部和状态显示

38-【C端】订单详情选择医院功能

39-【C端】订单详情选择就诊时间功能

40-【C端】订单详情剩余表单显示

41-【C端】订单下单和支付功能实现

42-【C端】订单列表显示效果实现

43-【C端】订单详情显示效果(上)

44-【C端】订单详情显示效果(下)

45-【C端】我的页面显示效果

46-【C端】订单列表页面实现效果

47-【C端】订单列表服务状态扭转和问题解决

48-【完结】项目总结和回顾


自存19-48》 是转载文章,点击查看原文


相关推荐


🔥 连八股文都不懂还指望在前端混下去么
Gaby2025/10/2

废话只说一句:码字不易求个👍,收藏 === 学会,快行动起来吧!🙇‍🙇‍🙇‍。2024.03.04 由于篇幅限制更详细的内容已更新到 ☞ 我的 GitHub 上,有纠正错误和需要补充的小伙伴可以在这里留言,我会及时更新上去的。推荐个API管理神器 Apipost 用过的都知道好使 1. HTTP 和 HTTPS 1.http 和 https 的基本概念 http: 是一个客户端和服务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的超文本传输协议。 http


学习日报 20250923|MQ (Kafka)面试深度复盘
靈臺清明2025/10/2

MQ 面试深度复盘:从实战经验到底层设计全解析 在分布式系统架构面试中,消息队列(MQ)是考察候选人技术深度与实战经验的核心模块之一。本文基于真实面试场景,从 MQ 的实际应用、核心价值、产品选型、故障排查到架构设计,进行全面复盘总结,既适合面试备考记忆,也可作为技术文章发布,帮助更多开发者梳理 MQ 知识体系。 一、基础认知:你真的懂 MQ 的 “用武之地” 吗? 面试中,面试官往往从 “是否用过 MQ” 切入,逐步深入到 “为什么用”,核心是考察候选人对 MQ 核心价值的理解是否停留在


桌面预测类开发,桌面%性别,姓名预测%系统开发,基于python,scikit-learn机器学习算法(sklearn)实现,分类算法,CSV无数据库
合作小小程序员小小店10/1/2025

这一个也是和信号识别的那个项目demo一样。桌面很常用的开发框架tkinter,在没有pyqt之前一直用着,帮客户修改一下代码。人工智能应用开发套路还是一样,从csv获取数据集,进行数据集清洗去重等操作,完成数据清洗就可以构造模型进行模型fit了,最后模型预测评估调优。


常见开发语言在 Windows 上的默认编码格式
十启树9/30/2025

字符串是字节流,输出编码取决于手动设置(如 header 或 mb_* 函数)


基于LazyLLM多Agent大模型应用的开发框架,搭建本地大模型AI工具,你贴身的写作、论文小助手
xcLeigh2025/10/4

在搭建本地大模型作为写作、论文小助手时,开发者常面临诸多技术难题:模型部署需研究复杂 API 服务,微调模型要应对框架选择与模型切换的困扰,工具落地还需掌握 Web 开发技能,这让初级开发者望而却步,资深专家也需为适配需求、集成新工具耗费大量精力。而 LazyLLM 多 Agent 大模型应用开发框架可有效解决这些问题,它打包了应用搭建、数据准备、模型部署、微调、评测等全环节工具。初级开发者借助预置组件即可打造有生产价值的 AI 工具,资深专家能依托其模块化设计集成自有算法与前沿工具,助力不同水


【Docker】说说卷挂载与绑定挂载
你的人类朋友2025/10/5

前言 我最开始接触 Docker 的时候,遇到 mysql 这样的容器,我一般使用卷挂载。它的好处就是将挂载的位置交给 Docker 管理,我们规定卷的名字即可,不需要关心挂载的位置。我感觉这样很方便,所以后面我基本一遇到挂载就用卷挂载。 但是最近,我慢慢地开始喜欢上绑定挂载了。特别是要部署一个什么环境之类的【如 n8n、redis】,都会优先使用绑定挂载。这个挂载方式会让我更有一种掌控感。 今天就来总结这两种挂载方式的相关知识。 正文 一、什么是 Docker 数据挂载? 在 Docker 中


【OpenCV】图像处理入门:从基础到实战技巧
朋鱼燕2025/10/6

目录 1.对图像的基本理解 2.数据读取-图像 ​编辑 3.数据读取-视频 4.ROI区域 1.对图像的基本理解 图像是由一个个像素点组成的,RGB图像有三个通道,而灰度图像只有一个通道 RGB每个通道的像素点的值的范围是0-255,数值越大,对应该颜色通道的亮度越亮 2.数据读取-图像 在文件的路径下读取一张图像,不能包含中文 opencv的读取格式是BGR cv2.waitKey(0)按下任意键才关闭图像,换成1000的话是显示1000


cygwin + redis
欧的曼2025/10/8

1. 下载 Redis 源码 推荐安装稳定版(如 Redis 7.0.12,可从 Redis 官网下载页 获取最新稳定版链接): wget https://download.redis.io/releases/redis-7.0.12.tar.gz 2. 解压并进入源码目录 3. 编译 Redis(关键步骤) 找到Cygwin安装目录下的usr\include\dlfcn.h文件,修改如下代码,将#if __GNU_VISIBLE、#endif 这两行注释掉。(使用// 或 /


从入门到实战:全面解析Protobuf的安装配置、语法规范与高级应用——手把手教你用Protobuf实现高效数据序列化与跨语言通信
羑悻的小杀马特.2025/10/9

文章目录 本篇摘要一.`Protocol Buffers(Protobuf)`简介1. **核心定义**2. **核心作用**3. **对比优势**4. **使用关键点**总结 二.`基于windows及ubuntu22.04安装Protobuf``windows`ubuntu22.04 三.快速上手protobuf编写及测试规范说明编译命令编译生成结果 四.proto3语法解析之字段规则与消息定义五. `Protobuf 命令行decode操作`六.仓库链接七.本篇


基于单片机的Boost升压斩波电源电路
清风6666662025/10/11

基于单片机的Boost升压斩波电源电路设计 点击链接下载资料:https://download.csdn.net/download/m0_51061483/92081480 1. 系统功能概述 本系统以单片机为核心控制单元,设计并实现了一种Boost升压型斩波电源电路。系统能够实现输入5V电压,通过Boost电路升压至可调的20V输出范围。用户可通过按键设置目标输出电压,液晶LCD模块实时显示当前输出电压与设定电压,形成完整的闭环控制系统。 系统采用PWM控制技术与DA(数模转换)调

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0