Compare commits
	
		
			2 Commits
		
	
	
		
			d4457b9f30
			...
			1ce760c489
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1ce760c489 | |||
| 4dd21ebc53 | 
| @ -97,7 +97,7 @@ export function useRouterPush(inSetup = true) { | ||||
|     const redirect = route.value.query?.redirect as string; | ||||
|     console.log(redirect); | ||||
|     if (needRedirect && redirect) { | ||||
|       await routerPush(redirect); | ||||
|       await toHome(); | ||||
|     } else { | ||||
|       await toHome(); | ||||
|     } | ||||
|  | ||||
| @ -41,16 +41,26 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl | ||||
|     transformer: res => { | ||||
|       console.log(res); | ||||
|       const { data = [], current = 1, size = 10, total = 0 } = res || {}; | ||||
| 
 | ||||
|       // Ensure that the size is greater than 0, If it is less than 0, it will cause paging calculation errors.
 | ||||
|       console.log(total); | ||||
|       const pageSize = size <= 0 ? 10 : size; | ||||
| 
 | ||||
|       const recordsWithIndex = data.map((item, index) => { | ||||
|         return { | ||||
|           ...item, | ||||
|           index: (current - 1) * pageSize + index + 1 | ||||
|         }; | ||||
|       }); | ||||
|       if(data.data){ | ||||
|         var recordsWithIndex = data.data.map((item, index) => { | ||||
|           return { | ||||
|             ...item, | ||||
|             index: (current - 1) * pageSize + index + 1 | ||||
|           }; | ||||
|         }); | ||||
|       }else{ | ||||
|         var recordsWithIndex = data.map((item, index) => { | ||||
|           return { | ||||
|             ...item, | ||||
|             index: (current - 1) * pageSize + index + 1 | ||||
|           }; | ||||
|         }); | ||||
|       } | ||||
| 
 | ||||
|        | ||||
|       console.log(recordsWithIndex); | ||||
|       return { | ||||
|         data: recordsWithIndex, | ||||
|  | ||||
| @ -21,6 +21,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro | ||||
|   "iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"), | ||||
|   login: () => import("@/views/_builtin/login/index.vue"), | ||||
|   home: () => import("@/views/home/index.vue"), | ||||
|   system_auth: () => import("@/views/system/auth/index.vue"), | ||||
|   system_menu: () => import("@/views/system/menu/index.vue"), | ||||
|   system_user: () => import("@/views/system/user/index.vue"), | ||||
| }; | ||||
|  | ||||
| @ -84,6 +84,15 @@ export const generatedRoutes: GeneratedRoute[] = [ | ||||
|       i18nKey: 'route.system' | ||||
|     }, | ||||
|     children: [ | ||||
|       { | ||||
|         name: 'system_auth', | ||||
|         path: '/system/auth', | ||||
|         component: 'view.system_auth', | ||||
|         meta: { | ||||
|           title: 'system_auth', | ||||
|           i18nKey: 'route.system_auth' | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         name: 'system_menu', | ||||
|         path: '/system/menu', | ||||
|  | ||||
| @ -170,6 +170,7 @@ const routeMap: RouteMap = { | ||||
|   "iframe-page": "/iframe-page/:url", | ||||
|   "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?", | ||||
|   "system": "/system", | ||||
|   "system_auth": "/system/auth", | ||||
|   "system_menu": "/system/menu", | ||||
|   "system_user": "/system/user" | ||||
| }; | ||||
|  | ||||
| @ -24,6 +24,7 @@ export function createRouteGuard(router: Router) { | ||||
|       next(location); | ||||
|       return; | ||||
|     } | ||||
|     | ||||
| 
 | ||||
|     const authStore = useAuthStore(); | ||||
| 
 | ||||
| @ -37,19 +38,22 @@ export function createRouteGuard(router: Router) { | ||||
| 
 | ||||
|     const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role)); | ||||
|     const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole; | ||||
|     // if(!location){
 | ||||
|     //   next({ name: 'home' });
 | ||||
|     //   return;
 | ||||
|     // }
 | ||||
| 
 | ||||
|     // if it is login route when logged in, then switch to the root page
 | ||||
|     if (to.name === loginRoute && isLogin) { | ||||
|       next({ name: rootRoute }); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     | ||||
|     // if the route does not need login, then it is allowed to access directly
 | ||||
|     if (!needLogin) { | ||||
|       handleRouteSwitch(to, from, next); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // the route need login but the user is not logged in, then switch to the login page
 | ||||
|     if (!isLogin) { | ||||
|       next({ name: loginRoute, query: { redirect: to.fullPath } }); | ||||
| @ -164,6 +168,8 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw | ||||
| 
 | ||||
| function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) { | ||||
|   // route with href
 | ||||
|    | ||||
| 
 | ||||
|   if (to.meta.href) { | ||||
|     window.open(to.meta.href, '_blank'); | ||||
| 
 | ||||
|  | ||||
| @ -8,7 +8,6 @@ export const ROOT_ROUTE: CustomRoute = { | ||||
|   redirect: '/home', | ||||
|   meta: { | ||||
|     title: 'root', | ||||
|     constant: true | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										37
									
								
								src/service/api/group.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/service/api/group.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| import { request } from '../request'; | ||||
| 
 | ||||
| /** 获取API权限组列表 */ | ||||
| export function fetchGetGroups(params?: Api.Common.CommonSearchParams) { | ||||
|   return request<GroupApi.GroupListResponse>({  | ||||
|     url: '/adminapi/group/index', | ||||
|     method: 'get', | ||||
|     params | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| /** 添加API权限组 */ | ||||
| export function addGroup(data: Partial<GroupApi.Group>) { | ||||
|   return request({ | ||||
|     url: '/adminapi/group/add', | ||||
|     method: 'post', | ||||
|     data | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| /** 编辑API权限组 */ | ||||
| export function editGroup(data: Partial<GroupApi.Group>) { | ||||
|   return request({ | ||||
|     url: `/adminapi/group/edit/ids/${data.id}`, | ||||
|     method: 'post', | ||||
|     data | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| /** 删除API权限组 */ | ||||
| export function deleteGroup(id) { | ||||
|   return request({ | ||||
|     url: `/adminapi/group/del/ids`, | ||||
|     method: 'post', | ||||
|     data:{ids:id} | ||||
|   }); | ||||
| }  | ||||
| @ -1,2 +1,39 @@ | ||||
| export * from './auth'; | ||||
| export * from './route'; | ||||
| 
 | ||||
| import { request } from '../request'; | ||||
| 
 | ||||
| 
 | ||||
| /** 添加API权限组 */ | ||||
| export function fetchGetAdminList(data) { | ||||
|     return request({ | ||||
|         url: '/adminapi/admin_manager/index', | ||||
|         method: 'post', | ||||
|         data | ||||
|     }); | ||||
| } | ||||
| /** 添加API权限组 */ | ||||
| export function addAdmin(data,type) { | ||||
|     return request({ | ||||
|         url: '/adminapi/admin_manager/add', | ||||
|         method:type, | ||||
|         data | ||||
|     }); | ||||
| } | ||||
| /** 添加API权限组 */ | ||||
| export function editAdmin(data) { | ||||
|     return request({ | ||||
|         url: '/adminapi/admin_manager/edit/ids', | ||||
|         method: 'post', | ||||
|         data | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| /** 添加API权限组 */ | ||||
| export function deleteAdmin(data) { | ||||
|     return request({ | ||||
|         url: '/adminapi/admin_manager/del/ids', | ||||
|         method: 'post', | ||||
|         data | ||||
|     }); | ||||
| } | ||||
| @ -7,7 +7,7 @@ export function fetchGetConstantRoutes() { | ||||
| 
 | ||||
| /** get user routes */ | ||||
| export function fetchGetUserRoutes() { | ||||
|   return request<Api.Route.UserRoute>({ url: '/adminapi/rule/index?is_tree=1' }); | ||||
|   return request<Api.Route.UserRoute>({ url: '/adminapi/admin/menu?is_tree=1' }); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -10,6 +10,8 @@ import { $t } from '@/locales'; | ||||
| import { useRouteStore } from '../route'; | ||||
| import { useTabStore } from '../tab'; | ||||
| import { clearAuthStorage, getToken } from './shared'; | ||||
| import { router } from '@/router'; | ||||
| 
 | ||||
| 
 | ||||
| export const useAuthStore = defineStore(SetupStoreId.Auth, () => { | ||||
|   const route = useRoute(); | ||||
| @ -69,8 +71,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => { | ||||
|       const pass = await loginByToken(response.data.data.userinfo); | ||||
| 
 | ||||
|       if (pass) { | ||||
|         await redirectFromLogin(redirect); | ||||
| 
 | ||||
|         //await redirectFromLogin(redirect);
 | ||||
|         router.push('home'); | ||||
|         window.$notification?.success({ | ||||
|           title: $t('page.login.common.loginSuccess'), | ||||
|           content: $t('page.login.common.welcomeBack', { userName: response.data.data.userinfo.nickname }), | ||||
|  | ||||
| @ -299,14 +299,13 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => { | ||||
|    */ | ||||
|   function handleUpdateRootRouteRedirect(redirectKey: LastLevelRouteKey) { | ||||
|     const redirect = getRoutePath(redirectKey); | ||||
| 
 | ||||
|     | ||||
|     if (redirect) { | ||||
|       const rootRoute: CustomRoute = { ...ROOT_ROUTE, redirect }; | ||||
| 
 | ||||
|       router.removeRoute(rootRoute.name); | ||||
| 
 | ||||
|       const [rootVueRoute] = getAuthVueRoutes([rootRoute]); | ||||
| 
 | ||||
|       router.addRoute(rootVueRoute); | ||||
|     } | ||||
|   } | ||||
|  | ||||
							
								
								
									
										2
									
								
								src/typings/elegant-router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/typings/elegant-router.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -24,6 +24,7 @@ declare module "@elegant-router/types" { | ||||
|     "iframe-page": "/iframe-page/:url"; | ||||
|     "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"; | ||||
|     "system": "/system"; | ||||
|     "system_auth": "/system/auth"; | ||||
|     "system_menu": "/system/menu"; | ||||
|     "system_user": "/system/user"; | ||||
|   }; | ||||
| @ -86,6 +87,7 @@ declare module "@elegant-router/types" { | ||||
|     | "iframe-page" | ||||
|     | "login" | ||||
|     | "home" | ||||
|     | "system_auth" | ||||
|     | "system_menu" | ||||
|     | "system_user" | ||||
|   >; | ||||
|  | ||||
							
								
								
									
										32
									
								
								src/typings/group.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/typings/group.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| /** | ||||
|  * 权限组相关类型定义 | ||||
|  */ | ||||
| declare namespace GroupApi { | ||||
|   /** 权限组数据类型 */ | ||||
|   interface Group { | ||||
|     /** 权限组ID */ | ||||
|     id: number; | ||||
|     /** 权限组名称 */ | ||||
|     name: string; | ||||
|     /** 规则ID */ | ||||
|     rules: string; | ||||
|     /** 创建时间 */ | ||||
|     createtime: string; | ||||
|     /** 更新时间 */ | ||||
|     updatetime: string; | ||||
|     /** 状态 */ | ||||
|     status: string; | ||||
|   } | ||||
| 
 | ||||
|   /** 权限组列表响应 */ | ||||
|   interface GroupListResponse { | ||||
|     /** 权限组列表 */ | ||||
|     data: Group[]; | ||||
|     /** 当前页码 */ | ||||
|     current: number; | ||||
|     /** 每页条数 */ | ||||
|     size: number; | ||||
|     /** 总条数 */ | ||||
|     total: number; | ||||
|   } | ||||
| }  | ||||
							
								
								
									
										278
									
								
								src/views/system/auth/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								src/views/system/auth/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,278 @@ | ||||
| <script setup lang="tsx"> | ||||
| import { ref } from 'vue'; | ||||
| import type { Ref } from 'vue'; | ||||
| import { NButton, NPopconfirm, NTag, NTreeSelect } from 'naive-ui'; | ||||
| import { useBoolean } from '@sa/hooks'; | ||||
| import { useAppStore } from '@/store/modules/app'; | ||||
| import { useTable, useTableOperate } from '@/hooks/common/table'; | ||||
| import { $t } from '@/locales'; | ||||
| import { yesOrNoRecord } from '@/constants/common'; | ||||
| import SvgIcon from '@/components/custom/svg-icon.vue'; | ||||
| import { fetchGetGroups, addGroup, editGroup, deleteGroup } from '@/service/api/group'; | ||||
| import { fetchGetUserRoutes } from '@/service/api/route'; | ||||
| 
 | ||||
| const appStore = useAppStore(); | ||||
| 
 | ||||
| const { bool: visible, setTrue: openModal } = useBoolean(); | ||||
| 
 | ||||
| const wrapperRef = ref<HTMLElement | null>(null); | ||||
| const title = ref(''); | ||||
| const rulesTree = ref<any[]>([]); | ||||
| const rulesLoading = ref(false); | ||||
| 
 | ||||
| const model = ref<Partial<GroupApi.Group & { rulesArray: (string | number)[] }>>({ | ||||
|     id: 0, | ||||
|     name: '', | ||||
|     rules: '', | ||||
|     status: 'normal', | ||||
|     rulesArray: [] | ||||
| }); | ||||
| const rules = {}; | ||||
| 
 | ||||
| const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useTable({ | ||||
|     apiFn: fetchGetGroups, | ||||
|     columns: () => [ | ||||
|         { | ||||
|             key: 'id', | ||||
|             title: 'ID', | ||||
|             align: 'center', | ||||
|             width: 100, | ||||
|         }, | ||||
|         { | ||||
|             key: 'name', | ||||
|             title: '权限组名称', | ||||
|             align: 'center', | ||||
|             width: 250, | ||||
|         }, | ||||
|         { | ||||
|             key: 'rules', | ||||
|             title: '规则ID', | ||||
|             align: 'center', | ||||
|             width: 300, | ||||
|         }, | ||||
|         { | ||||
|             key: 'createtime', | ||||
|             title: '创建时间', | ||||
|             align: 'center', | ||||
|             width: 200, | ||||
|         }, | ||||
|         { | ||||
|             key: 'updatetime', | ||||
|             title: '更新时间', | ||||
|             align: 'center', | ||||
|             width: 200, | ||||
|         }, | ||||
|         { | ||||
|             key: 'status', | ||||
|             title: '状态', | ||||
|             align: 'center', | ||||
|             width: 120, | ||||
|             render: (row) => { | ||||
|                 const status: CommonType.YesOrNo = row.status === 'hidden' ? 'N' : 'Y'; | ||||
| 
 | ||||
|                 const tagMap: Record<CommonType.YesOrNo, NaiveUI.ThemeColor> = { | ||||
|                     Y: 'error', | ||||
|                     N: 'default' | ||||
|                 }; | ||||
| 
 | ||||
|                 const label = $t(yesOrNoRecord[status]); | ||||
| 
 | ||||
|                 return <NTag type={ tagMap[status] }> { label } </NTag>; | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             key: 'operate', | ||||
|             title: '操作', | ||||
|             align: 'center', | ||||
|             width: 230, | ||||
|             render: (row) => ( | ||||
|                 <div class= "flex-center justify-end gap-8px" > | ||||
|                 <NButton type="primary" ghost size="small" onClick={() => handleEdit(row)}> | ||||
|                     编辑 | ||||
|                     </NButton> | ||||
|                     < NPopconfirm onPositiveClick = {() => handleDelete(row.id as number)}> | ||||
|                         {{ | ||||
|               default: () => '确定删除吗?', | ||||
|         trigger: () => ( | ||||
|             <NButton type= "error" ghost size = "small" > | ||||
|                 删除 | ||||
|                 </NButton> | ||||
|               ) | ||||
| }} | ||||
| </NPopconfirm> | ||||
|     </div> | ||||
|       ) | ||||
|     } | ||||
|   ] | ||||
| }); | ||||
| 
 | ||||
| const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData); | ||||
| 
 | ||||
| const operateType = ref('add'); | ||||
| 
 | ||||
| function handleAdd() { | ||||
|     operateType.value = 'add'; | ||||
|     title.value = '新增API权限组'; | ||||
|     // 清空model | ||||
|     model.value = { | ||||
|         id: 0, | ||||
|         name: '', | ||||
|         rules: '', | ||||
|         status: 'normal', | ||||
|         rulesArray: [] | ||||
|     }; | ||||
|     openModal(); | ||||
| } | ||||
| 
 | ||||
| async function handleDelete(id: number) { | ||||
|     // request | ||||
|     const { response } = await deleteGroup(id); | ||||
|     console.log(response); | ||||
|     if (response?.data?.code === 1) { | ||||
|         window.$message?.success('删除成功!'); | ||||
|         getData(); | ||||
|     } else { | ||||
|         window.$message?.error(response?.data?.msg || '删除失败'); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function handleEdit(item: any) { | ||||
|     operateType.value = 'edit'; | ||||
|     title.value = '编辑API权限组'; | ||||
|     model.value = { | ||||
|         id: item.id, | ||||
|         name: item.name, | ||||
|         rules: item.rules, | ||||
|         status: item.status, | ||||
|         rulesArray: rulesStrToArray(item.rules) | ||||
|     }; | ||||
|     openModal(); | ||||
| } | ||||
| 
 | ||||
| function init() { | ||||
|     initRulesTree(); | ||||
| } | ||||
| 
 | ||||
| // 创建获取规则树数据的函数 | ||||
| async function initRulesTree() { | ||||
|     rulesLoading.value = true; | ||||
|     try { | ||||
|         const { response } = await fetchGetUserRoutes(); | ||||
|         if (response?.data?.code === 1) { | ||||
|             // 假设接口返回的数据结构需要转换成tree格式 | ||||
|             rulesTree.value = formatRulesTree(response.data.data || []); | ||||
|         } else { | ||||
|             window.$message?.error(response?.data?.msg || '获取规则列表失败'); | ||||
|         } | ||||
|     } catch (error) { | ||||
|         console.error('获取规则列表失败', error); | ||||
|         window.$message?.error('获取规则列表失败'); | ||||
|     } finally { | ||||
|         rulesLoading.value = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // 转换规则树数据格式的函数 | ||||
| function formatRulesTree(data: any[]) { | ||||
|     // 根据实际API返回结构进行格式转换 | ||||
|     // 这里假设API返回的数据结构已经是合适的树形结构 | ||||
|     // 可能需要根据实际情况调整键名,如把id改为key,把name改为label等 | ||||
|     return data.map(item => ({ | ||||
|         key: item.id, | ||||
|         label: item.title || item.name, | ||||
|         children: item.children ? formatRulesTree(item.children) : undefined | ||||
|     })); | ||||
| } | ||||
| 
 | ||||
| // ID字符串转数组的函数 | ||||
| function rulesStrToArray(rulesStr: string): (string | number)[] { | ||||
|     if (!rulesStr) return []; | ||||
|     return rulesStr.split(',').map(item => { | ||||
|         // 如果ID是数字类型,则转换为数字 | ||||
|         const numId = Number(item); | ||||
|         return isNaN(numId) ? item : numId; | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| // ID数组转字符串的函数 | ||||
| function rulesArrayToStr(rulesArray: (string | number)[]): string { | ||||
|     if (!rulesArray || !rulesArray.length) return ''; | ||||
|     return rulesArray.join(','); | ||||
| } | ||||
| 
 | ||||
| // init | ||||
| init(); | ||||
| 
 | ||||
| async function handleSubmit() { | ||||
|     // 在提交前将规则数组转换为字符串 | ||||
|     if (model.value.rulesArray) { | ||||
|         model.value.rules = rulesArrayToStr(model.value.rulesArray); | ||||
|     } | ||||
|      | ||||
|     let res; | ||||
|     if (operateType.value === 'add') { | ||||
|         res = await addGroup(model.value); | ||||
|     } else { | ||||
|         res = await editGroup(model.value); | ||||
|     } | ||||
|     console.log('response: ', res.response); | ||||
|     if (res.response?.data?.code === 1) { | ||||
|         window.$message?.success(res.response.data.msg); | ||||
|         visible.value = false; | ||||
|         getData(); | ||||
|     } else { | ||||
|         window.$message?.error(res.response?.data?.msg || '操作失败'); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <div ref="wrapperRef" class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto"> | ||||
|         <NCard title="API权限组管理" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper"> | ||||
|             <template #header-extra> | ||||
|                 <TableHeaderOperation v-model:columns="columnChecks" :disabled-delete="checkedRowKeys.length === 0" | ||||
|                     :loading="loading" @add="handleAdd" @refresh="getData" /> | ||||
|             </template> | ||||
|             <NDataTable v-model:checked-row-keys="checkedRowKeys" :columns="columns" :data="data" size="small" | ||||
|                 :flex-height="!appStore.isMobile" :scroll-x="1088" :loading="loading" :row-key="row => row.id" remote | ||||
|                 :pagination="pagination" class="sm:h-full" /> | ||||
|         </NCard> | ||||
|         <NModal v-model:show="visible" :title="title" preset="card" class="w-800px"> | ||||
|             <NScrollbar class="h-480px pr-20px"> | ||||
|                 <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="100"> | ||||
|                     <NGrid responsive="screen" item-responsive> | ||||
|                         <NFormItemGi span="24" label="权限组名称" path="name"> | ||||
|                             <NInput v-model:value="model.name" placeholder="请输入权限组名称" /> | ||||
|                         </NFormItemGi> | ||||
|                         <NFormItemGi span="24" label="规则ID" path="rulesArray"> | ||||
|                             <NTreeSelect | ||||
|                                 v-model:value="model.rulesArray" | ||||
|                                 placeholder="请选择规则" | ||||
|                                 :options="rulesTree" | ||||
|                                 :loading="rulesLoading" | ||||
|                                 multiple | ||||
|                                 cascade | ||||
|                                 checkable | ||||
|                                 clearable | ||||
|                             /> | ||||
|                         </NFormItemGi> | ||||
|                         <NFormItemGi span="24 m:12" label="状态" path="status"> | ||||
|                             <NRadioGroup v-model:value="model.status"> | ||||
|                                 <NRadio value="hidden" label="禁用" /> | ||||
|                                 <NRadio value="normal" label="正常" /> | ||||
|                             </NRadioGroup> | ||||
|                         </NFormItemGi> | ||||
|                     </NGrid> | ||||
|                 </NForm> | ||||
|             </NScrollbar> | ||||
|             <template #footer> | ||||
|                 <NSpace justify="end" :size="16"> | ||||
|                     <NButton @click="visible = false">取消</NButton> | ||||
|                     <NButton type="primary" @click="handleSubmit">确认</NButton> | ||||
|                 </NSpace> | ||||
|             </template> | ||||
|         </NModal> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped></style> | ||||
| @ -1,7 +1,220 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <h1>用户管理</h1> | ||||
|   <div ref="wrapperRef" class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto"> | ||||
|     <NCard title="管理员管理" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper"> | ||||
|       <template #header-extra> | ||||
|         <TableHeaderOperation v-model:columns="columnChecks" :disabled-delete="checkedRowKeys.length === 0" | ||||
|           :loading="loading" @add="handleAdd" @refresh="getData" /> | ||||
|       </template> | ||||
|       <NDataTable v-model:checked-row-keys="checkedRowKeys" :columns="columns" :data="data" size="small" | ||||
|         :flex-height="!appStore.isMobile" :scroll-x="1088" :loading="loading" :row-key="row => row.id" remote | ||||
|         :pagination="pagination" class="sm:h-full" /> | ||||
|     </NCard> | ||||
|     <NModal v-model:show="visible" :title="title" preset="card" class="w-800px"> | ||||
|       <NScrollbar class="h-480px pr-20px"> | ||||
|         <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="100"> | ||||
|           <NGrid responsive="screen" item-responsive> | ||||
|             <NFormItemGi span="24 m:12" label="用户名" path="username"> | ||||
|               <NInput v-model:value="model.username" placeholder="请输入用户名" /> | ||||
|             </NFormItemGi> | ||||
|             <NFormItemGi span="24 m:12" label="昵称" path="nickname"> | ||||
|               <NInput v-model:value="model.nickname" placeholder="请输入昵称" /> | ||||
|             </NFormItemGi> | ||||
|             <NFormItemGi span="24 m:12" label="密码" path="password"> | ||||
|               <NInput v-model:value="model.password" type="password" placeholder="请输入密码" /> | ||||
|             </NFormItemGi> | ||||
|             <NFormItemGi span="24 m:12" label="用户组" path="group"> | ||||
|               <NSelect v-model:value="model.group" :options="groupOptions" placeholder="请选择用户组" /> | ||||
|             </NFormItemGi> | ||||
|             <NFormItemGi span="24 m:12" label="邮箱" path="email"> | ||||
|               <NInput v-model:value="model.email" placeholder="请输入邮箱" /> | ||||
|             </NFormItemGi> | ||||
|             <NFormItemGi span="24 m:12" label="手机号" path="mobile"> | ||||
|               <NInput v-model:value="model.mobile" placeholder="请输入手机号" /> | ||||
|             </NFormItemGi> | ||||
|           </NGrid> | ||||
|         </NForm> | ||||
|       </NScrollbar> | ||||
|       <template #footer> | ||||
|         <NSpace justify="end" :size="16"> | ||||
|           <NButton @click="visible = false">取消</NButton> | ||||
|           <NButton type="primary" @click="handleSubmit">确认</NButton> | ||||
|         </NSpace> | ||||
|       </template> | ||||
|     </NModal> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="tsx"> | ||||
| import { ref, onMounted } from 'vue'; | ||||
| import type { Ref } from 'vue'; | ||||
| import { NButton, NPopconfirm, NTag, NSelect } from 'naive-ui'; | ||||
| import { useBoolean } from '@sa/hooks'; | ||||
| import { useAppStore } from '@/store/modules/app'; | ||||
| import { useTable, useTableOperate } from '@/hooks/common/table'; | ||||
| import { fetchGetAdminList, addAdmin, editAdmin, deleteAdmin } from '@/service/api/index'; | ||||
| import { fetchGetGroups } from '@/service/api/group'; | ||||
| import { log } from 'console'; | ||||
| 
 | ||||
| const appStore = useAppStore(); | ||||
| 
 | ||||
| const { bool: visible, setTrue: openModal } = useBoolean(); | ||||
| 
 | ||||
| const wrapperRef = ref<HTMLElement | null>(null); | ||||
| const title = ref(''); | ||||
| 
 | ||||
| const groupOptions = ref<{ label: string; value: number }[]>([]); | ||||
| 
 | ||||
| // 获取用户组数据 | ||||
| async function getGroupOptions() { | ||||
|   const { response } = await fetchGetGroups(); | ||||
|   console.log(response.data) | ||||
|   if (response.data.code == 1) { | ||||
|     const data = (response.data).data.data; | ||||
|     console.log(data); | ||||
|     groupOptions.value = data.map((item) => ({ | ||||
|       label: item.name, | ||||
|       value: item.id | ||||
|     })); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   console.log(111111111); | ||||
|   getGroupOptions(); | ||||
| }); | ||||
| const model = ref({ | ||||
|   username: '', | ||||
|   nickname: '', | ||||
|   password: '', | ||||
|   group: null, | ||||
|   email: '', | ||||
|   mobile: '', | ||||
|   id: 0, | ||||
| }); | ||||
| const rules = {}; | ||||
| const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useTable({ | ||||
|   apiFn: fetchGetAdminList, | ||||
|   columns: () => [ | ||||
|     { | ||||
|       key: 'id', | ||||
|       title: 'ID', | ||||
|       align: 'center', | ||||
|       width: 100, | ||||
|     }, | ||||
|     { | ||||
|       key: 'username', | ||||
|       title: '用户名', | ||||
|       align: 'center', | ||||
|       width: 150, | ||||
|     }, | ||||
|     { | ||||
|       key: 'nickname', | ||||
|       title: '昵称', | ||||
|       align: 'center', | ||||
|       width: 150, | ||||
|     }, | ||||
|     { | ||||
|       key: 'groups_text', | ||||
|       title: '用户组', | ||||
|       align: 'center', | ||||
|       width: 150, | ||||
|     }, | ||||
|     { | ||||
|       key: 'email', | ||||
|       title: '邮箱', | ||||
|       align: 'center', | ||||
|       width: 200, | ||||
|     }, | ||||
|     { | ||||
|       key: 'mobile', | ||||
|       title: '手机号', | ||||
|       align: 'center', | ||||
|       width: 150, | ||||
|     }, | ||||
|     { | ||||
|       key: 'operate', | ||||
|       title: '操作', | ||||
|       align: 'center', | ||||
|       width: 230, | ||||
|       render: row => ( | ||||
|         <div class="flex-center justify-end gap-8px"> | ||||
|           <NButton type="primary" ghost size="small" onClick={() => handleEdit(row)}> | ||||
|             编辑 | ||||
|           </NButton> | ||||
|           <NPopconfirm onPositiveClick={() => handleDelete(row.id)}> | ||||
|             {{ | ||||
|               default: () => '确定删除吗?', | ||||
|               trigger: () => ( | ||||
|                 <NButton type="error" ghost size="small"> | ||||
|                   删除 | ||||
|                 </NButton> | ||||
|               ) | ||||
|             }} | ||||
|           </NPopconfirm> | ||||
|         </div> | ||||
|       ) | ||||
|     } | ||||
|   ] | ||||
| }); | ||||
| 
 | ||||
| const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData); | ||||
| 
 | ||||
| const operateType = ref('add'); | ||||
| 
 | ||||
| function handleAdd() { | ||||
|   operateType.value = 'add'; | ||||
|   title.value = '新增管理员'; | ||||
|   // 清空model | ||||
|   model.value = { | ||||
|     username: '', | ||||
|     nickname: '', | ||||
|     password: '', | ||||
|     group: null, | ||||
|     email: '', | ||||
|     mobile: '', | ||||
|     id: 0, | ||||
|   }; | ||||
|   openModal(); | ||||
| } | ||||
| 
 | ||||
| async function handleDelete(id: number) { | ||||
|   // request | ||||
|   const { response } = await deleteAdmin(id); | ||||
|   if (response?.data?.code === 1) { | ||||
|     window.$message?.success('删除成功!'); | ||||
|     getData(); | ||||
|   } else { | ||||
|     window.$message?.error(response?.data?.msg || '删除失败'); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function handleEdit(item: any) { | ||||
|   console.log(item); | ||||
|   //item. | ||||
|   operateType.value = 'edit'; | ||||
|   title.value = '编辑管理员'; | ||||
|   model.value = { ...item }; | ||||
|   openModal(); | ||||
| } | ||||
| 
 | ||||
| async function handleSubmit() { | ||||
|   let res; | ||||
|   if (operateType.value === 'add') { | ||||
|     res = await addAdmin(model.value,'post'); | ||||
|   } else { | ||||
|     res = await editAdmin(model.value); | ||||
|   } | ||||
|   if (res.response?.data?.code === 1) { | ||||
|     window.$message?.success(res.response.data.msg); | ||||
|     visible.value = false; | ||||
|     getData(); | ||||
|   } else { | ||||
|     window.$message?.error(res.response?.data?.msg || '操作失败'); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style scoped></style> | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user