mirror of
				https://github.com/pure-admin/pure-admin-thin.git
				synced 2025-10-31 15:44:49 +08:00 
			
		
		
		
	feat: 新增角色管理、岗位管理
This commit is contained in:
		
							parent
							
								
									6719fd8290
								
							
						
					
					
						commit
						b068518ba6
					
				| @ -1,4 +1,5 @@ | |||||||
| import { http } from "@/utils/http"; | import { http } from "@/utils/http"; | ||||||
|  | import { Tree } from "@/utils/tree"; | ||||||
| 
 | 
 | ||||||
| export interface MenuQuery { | export interface MenuQuery { | ||||||
|   isButton: boolean; |   isButton: boolean; | ||||||
| @ -7,7 +8,7 @@ export interface MenuQuery { | |||||||
| /** | /** | ||||||
|  * MenuDTO |  * MenuDTO | ||||||
|  */ |  */ | ||||||
| export interface MenuDTO { | export interface MenuDTO extends Tree { | ||||||
|   createTime?: Date; |   createTime?: Date; | ||||||
|   isButton?: number; |   isButton?: number; | ||||||
|   id?: number; |   id?: number; | ||||||
|  | |||||||
							
								
								
									
										70
									
								
								src/api/system/post.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/api/system/post.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | import { http } from "@/utils/http"; | ||||||
|  | 
 | ||||||
|  | export interface PostListCommand extends BasePageQuery { | ||||||
|  |   postCode?: string; | ||||||
|  |   postName?: string; | ||||||
|  |   status?: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface PostPageResponse { | ||||||
|  |   createTime: string; | ||||||
|  |   postCode: string; | ||||||
|  |   postId: number; | ||||||
|  |   postName: string; | ||||||
|  |   postSort: number; | ||||||
|  |   remark: string; | ||||||
|  |   status: number; | ||||||
|  |   statusStr: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getPostListApi(params: PostListCommand) { | ||||||
|  |   return http.request<ResponseData<PageDTO<PostPageResponse>>>( | ||||||
|  |     "get", | ||||||
|  |     "/system/post/list", | ||||||
|  |     { | ||||||
|  |       params | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const exportPostExcelApi = ( | ||||||
|  |   params: PostListCommand, | ||||||
|  |   fileName: string | ||||||
|  | ) => { | ||||||
|  |   return http.download("/system/post/excel", fileName, { | ||||||
|  |     params | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const deletePostApi = (data: Array<number>) => { | ||||||
|  |   return http.request<ResponseData<void>>("delete", "/system/post", { | ||||||
|  |     params: { | ||||||
|  |       // 需要将数组转换为字符串  否则Axios会将参数变成 noticeIds[0]:1  noticeIds[1]:2 这种格式,后端接收参数不成功
 | ||||||
|  |       ids: data.toString() | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export interface AddPostCommand { | ||||||
|  |   postCode: string; | ||||||
|  |   postName: string; | ||||||
|  |   postSort: number; | ||||||
|  |   remark?: string; | ||||||
|  |   status?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const addPostApi = (data: AddPostCommand) => { | ||||||
|  |   return http.request<ResponseData<void>>("post", "/system/post", { | ||||||
|  |     data | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export interface UpdatePostCommand extends AddPostCommand { | ||||||
|  |   postId: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const updatePostApi = (data: UpdatePostCommand) => { | ||||||
|  |   return http.request<ResponseData<void>>("put", "/system/post", { | ||||||
|  |     data | ||||||
|  |   }); | ||||||
|  | }; | ||||||
							
								
								
									
										65
									
								
								src/api/system/role.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/api/system/role.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | import { http } from "@/utils/http"; | ||||||
|  | 
 | ||||||
|  | export interface RoleQuery extends BasePageQuery { | ||||||
|  |   roleKey?: string; | ||||||
|  |   roleName?: string; | ||||||
|  |   status?: string; | ||||||
|  |   timeRangeColumn?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface RoleDTO { | ||||||
|  |   createTime: Date; | ||||||
|  |   dataScope: number; | ||||||
|  |   remark: string; | ||||||
|  |   roleId: number; | ||||||
|  |   roleKey: string; | ||||||
|  |   roleName: string; | ||||||
|  |   roleSort: number; | ||||||
|  |   selectedDeptList: number[]; | ||||||
|  |   selectedMenuList: number[]; | ||||||
|  |   status: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getRoleListApi(params: RoleQuery) { | ||||||
|  |   return http.request<ResponseData<PageDTO<RoleDTO>>>( | ||||||
|  |     "get", | ||||||
|  |     "/system/role/list", | ||||||
|  |     { | ||||||
|  |       params | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getRoleInfoApi(roleId: number) { | ||||||
|  |   return http.request<ResponseData<RoleDTO>>("get", "/system/role/" + roleId); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface AddRoleCommand { | ||||||
|  |   dataScope?: string; | ||||||
|  |   menuIds: number[]; | ||||||
|  |   remark?: string; | ||||||
|  |   roleKey: string; | ||||||
|  |   roleName: string; | ||||||
|  |   roleSort: number; | ||||||
|  |   status?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function addRoleApi(data: AddRoleCommand) { | ||||||
|  |   return http.request<void>("post", "/system/role", { | ||||||
|  |     data | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface UpdateRoleCommand extends AddRoleCommand { | ||||||
|  |   roleId: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function updateRoleApi(data: UpdateRoleCommand) { | ||||||
|  |   return http.request<void>("put", "/system/role", { | ||||||
|  |     data | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function deleteRoleApi(roleId: number) { | ||||||
|  |   return http.request<void>("delete", "/system/role/" + roleId); | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								src/assets/svg/FullScreenMaximize.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/svg/FullScreenMaximize.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20"><g fill="none"><path d="M3 5a2 2 0 0 1 2-2h2a.5.5 0 0 1 0 1H5a1 1 0 0 0-1 1v2a.5.5 0 0 1-1 0V5zm9.5-1.5A.5.5 0 0 1 13 3h2a2 2 0 0 1 2 2v2a.5.5 0 0 1-1 0V5a1 1 0 0 0-1-1h-2a.5.5 0 0 1-.5-.5zm-9 9a.5.5 0 0 1 .5.5v2a1 1 0 0 0 1 1h2a.5.5 0 0 1 0 1H5a2 2 0 0 1-2-2v-2a.5.5 0 0 1 .5-.5zm13 0a.5.5 0 0 1 .5.5v2a2 2 0 0 1-2 2h-2a.5.5 0 0 1 0-1h2a1 1 0 0 0 1-1v-2a.5.5 0 0 1 .5-.5z" fill="currentColor"/></g></svg> | ||||||
| After Width: | Height: | Size: 508 B | 
							
								
								
									
										1
									
								
								src/assets/svg/FullScreenMinimize.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/svg/FullScreenMinimize.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none"><path d="M8.5 3.75a.75.75 0 0 0-1.5 0v2.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 0 0 1.5h2.5A2.25 2.25 0 0 0 8.5 6.25v-2.5zm0 16.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 0-.75-.75h-2.5a.75.75 0 0 1 0-1.5h2.5a2.25 2.25 0 0 1 2.25 2.25v2.5zM16.25 3a.75.75 0 0 0-.75.75v2.5a2.25 2.25 0 0 0 2.25 2.25h2.5a.75.75 0 0 0 0-1.5h-2.5a.75.75 0 0 1-.75-.75v-2.5a.75.75 0 0 0-.75-.75zm-.75 17.25a.75.75 0 0 0 1.5 0v-2.5a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 0 0-1.5h-2.5a2.25 2.25 0 0 0-2.25 2.25v2.5z" fill="currentColor"/></g></svg> | ||||||
| After Width: | Height: | Size: 631 B | 
							
								
								
									
										173
									
								
								src/components/VDialog/VDialog.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/components/VDialog/VDialog.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,173 @@ | |||||||
|  | <template> | ||||||
|  |   <el-dialog | ||||||
|  |     v-model="visible" | ||||||
|  |     :fullscreen="fullScreen" | ||||||
|  |     class="v-dialog" | ||||||
|  |     :class="dialogClazz" | ||||||
|  |     :draggable="props.draggable" | ||||||
|  |     :show-close="false" | ||||||
|  |     v-bind="$attrs" | ||||||
|  |   > | ||||||
|  |     <template #header> | ||||||
|  |       <slot name="header"> | ||||||
|  |         <div | ||||||
|  |           style=" | ||||||
|  |             position: relative; | ||||||
|  |             box-sizing: border-box; | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             justify-content: center; | ||||||
|  |             height: 24px; | ||||||
|  |             font-weight: bold; | ||||||
|  |             color: rgb(118 131 164); | ||||||
|  |           " | ||||||
|  |         > | ||||||
|  |           <div v-text="props.title" /> | ||||||
|  |           <div | ||||||
|  |             style=" | ||||||
|  |               position: absolute; | ||||||
|  |               right: 0; | ||||||
|  |               display: flex; | ||||||
|  |               flex-direction: row; | ||||||
|  |               justify-content: center; | ||||||
|  |             " | ||||||
|  |           > | ||||||
|  |             <el-button | ||||||
|  |               v-if="props.showFullScreen" | ||||||
|  |               :icon="fullScreen ? FullScreenMinimize : FullScreenMaximize" | ||||||
|  |               link | ||||||
|  |               @click="requestFullScreen" | ||||||
|  |               class="header-btn" | ||||||
|  |             /> | ||||||
|  |             <el-button | ||||||
|  |               :icon="Close" | ||||||
|  |               link | ||||||
|  |               @click="handleCloseClick" | ||||||
|  |               class="header-btn" | ||||||
|  |               style="margin-left: 0" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </slot> | ||||||
|  |     </template> | ||||||
|  |     <template v-if="useBodyScrolling"> | ||||||
|  |       <el-scrollbar :max-height="bodyHeight" always> | ||||||
|  |         <slot name="default" /> | ||||||
|  |       </el-scrollbar> | ||||||
|  |     </template> | ||||||
|  |     <template v-else> | ||||||
|  |       <slot name="default" /> | ||||||
|  |     </template> | ||||||
|  | 
 | ||||||
|  |     <template #footer> | ||||||
|  |       <slot name="footer" v-if="!props.hiddenFooter"> | ||||||
|  |         <div style="display: flex; justify-content: center"> | ||||||
|  |           <el-button | ||||||
|  |             :loading="props.loading" | ||||||
|  |             type="primary" | ||||||
|  |             @click="handleConfirm" | ||||||
|  |             >{{ props.confirmText }}</el-button | ||||||
|  |           > | ||||||
|  |           <el-button :loading="props.loading" @click="handleCancel">{{ | ||||||
|  |             cancelText | ||||||
|  |           }}</el-button> | ||||||
|  |         </div> | ||||||
|  |       </slot> | ||||||
|  |     </template> | ||||||
|  |   </el-dialog> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { computed, ref } from "vue"; | ||||||
|  | import { ElDialog, ElButton, ElScrollbar } from "element-plus"; | ||||||
|  | import { DialogEmits, DialogProps } from "./dialog"; | ||||||
|  | import { Close } from "@element-plus/icons-vue"; | ||||||
|  | import FullScreenMaximize from "@/assets/svg/FullScreenMaximize.svg?component"; | ||||||
|  | import FullScreenMinimize from "@/assets/svg/FullScreenMinimize.svg?component"; | ||||||
|  | 
 | ||||||
|  | const props = withDefaults(defineProps<DialogProps>(), { | ||||||
|  |   fullScreen: undefined, | ||||||
|  |   confirmText: "确定", | ||||||
|  |   cancelText: "取消", | ||||||
|  |   disableFooter: false, | ||||||
|  |   useBodyScrolling: false, | ||||||
|  |   fixedBodyHeight: true, | ||||||
|  |   draggable: true, | ||||||
|  |   loading: false | ||||||
|  | }); | ||||||
|  | const emits = defineEmits<DialogEmits>(); | ||||||
|  | 
 | ||||||
|  | const visible = computed<boolean>({ | ||||||
|  |   get: () => { | ||||||
|  |     return props.modelValue; | ||||||
|  |   }, | ||||||
|  |   set: v => emits("update:modelValue", v) | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const fullScreenState = ref(!!props.initFullScreen); | ||||||
|  | const fullScreen = computed<boolean>({ | ||||||
|  |   get: () => { | ||||||
|  |     console.log("fullScreen getter", props.fullScreen, fullScreenState.value); | ||||||
|  |     // 非受控模式,状态完全由组件内部控制 | ||||||
|  |     if (props.fullScreen === undefined) { | ||||||
|  |       return fullScreenState.value; | ||||||
|  |     } else { | ||||||
|  |       return props.fullScreen; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   set: v => { | ||||||
|  |     fullScreenState.value = v; | ||||||
|  |     console.log("fullScreen setter", v, props.fullScreen); | ||||||
|  |     // 受控模式,将状态更新到父组件 | ||||||
|  |     if (props.fullScreen !== undefined) { | ||||||
|  |       emits("update:fullScreen", v); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // const fullScreen = ref<boolean>(false) | ||||||
|  | function requestFullScreen() { | ||||||
|  |   fullScreen.value = !fullScreen.value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const bodyHeight = computed(() => { | ||||||
|  |   const footerHeight = props.hiddenFooter ? "0" : "52px"; | ||||||
|  |   if (props.fullScreen) { | ||||||
|  |     // footerHeight=52,headerHeight=44,padding=12 | ||||||
|  |     return `calc(100dvh - ${footerHeight} - 44px)`; | ||||||
|  |   } else { | ||||||
|  |     return `calc(70dvh - ${footerHeight} - 44px)`; | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const dialogClazz = computed(() => { | ||||||
|  |   const classList: string[] = ["v-dialog"]; | ||||||
|  |   if (!props.fixedBodyHeight) { | ||||||
|  |     classList.push("flex-body"); | ||||||
|  |   } | ||||||
|  |   if (props.hiddenFooter) { | ||||||
|  |     classList.push("hidden-footer"); | ||||||
|  |   } | ||||||
|  |   return classList; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function handleConfirm() { | ||||||
|  |   emits("confirm"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleCancel() { | ||||||
|  |   emits("cancel"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleCloseClick() { | ||||||
|  |   visible.value = false; | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .header-btn :deep(.el-icon), | ||||||
|  | .header-btn :deep(.el-icon svg) { | ||||||
|  |   width: 24px; | ||||||
|  |   height: 24px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										62
									
								
								src/components/VDialog/dialog.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/components/VDialog/dialog.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | html.dark .v-dialog { | ||||||
|  |   --header-bg-color: #171d1e; | ||||||
|  |   --footer-bg-color: #171d1e; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog { | ||||||
|  |   --header-bg-color: #f5f7fa; | ||||||
|  |   --footer-bg-color: #f5f7fa; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog.hidden-footer .el-dialog__footer { | ||||||
|  |   padding: 0; | ||||||
|  |   border: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .el-dialog__footer { | ||||||
|  |   padding: 10px; | ||||||
|  |   /*border-top: 1px solid var(--el-border-color);*/ | ||||||
|  |   /*border-bottom: 1px solid var(--el-border-color);*/ | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   background-color: var(--header-bg-color); | ||||||
|  |   position: relative; /* 防止被表单覆盖底部 */ | ||||||
|  |   z-index: calc(var(--el-index-normal) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog { | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   margin: 15dvh auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog.is-fullscreen { | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   margin: auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog .el-dialog__header { | ||||||
|  |   padding: 10px 16px; | ||||||
|  |   /*border-bottom: 1px solid var(--el-border-color);*/ | ||||||
|  |   margin-right: 0; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   background-color: var(--header-bg-color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog--center .el-dialog__body, | ||||||
|  | .el-dialog__body { | ||||||
|  |   padding: 16px 20px; | ||||||
|  |   box-sizing: border-box; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog.is-fullscreen .el-dialog__body { | ||||||
|  |   height: calc(100dvh - 44px - 52px); | ||||||
|  |   max-height: calc(100dvh - 44px - 52px); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog .el-dialog__body { | ||||||
|  |   height: calc(70dvh - 44px - 52px); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .v-dialog.el-dialog.flex-body:not(.is-fullscreen) .el-dialog__body { | ||||||
|  |   height: initial; | ||||||
|  |   max-height: calc(70dvh - 44px - 52px); | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								src/components/VDialog/dialog.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/components/VDialog/dialog.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | export interface DialogProps { | ||||||
|  |   /** | ||||||
|  |    * 标题 | ||||||
|  |    */ | ||||||
|  |   title: string; | ||||||
|  |   /** | ||||||
|  |    * 显隐 | ||||||
|  |    */ | ||||||
|  |   modelValue: boolean; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 初始化全屏状态 | ||||||
|  |    */ | ||||||
|  |   initFullScreen?: boolean | undefined; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 展示全屏按钮 | ||||||
|  |    */ | ||||||
|  |   showFullScreen?: boolean; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 全屏 | ||||||
|  |    */ | ||||||
|  |   fullScreen?: boolean | undefined; | ||||||
|  |   confirmText?: string; | ||||||
|  |   cancelText?: string; | ||||||
|  |   loading?: boolean; | ||||||
|  |   /** | ||||||
|  |    * 使用el-scrollbar包裹对话框body | ||||||
|  |    */ | ||||||
|  |   useBodyScrolling?: boolean; | ||||||
|  |   /** | ||||||
|  |    * 固定对话框body高度 | ||||||
|  |    */ | ||||||
|  |   fixedBodyHeight?: boolean; | ||||||
|  | 
 | ||||||
|  |   draggable?: boolean; | ||||||
|  | 
 | ||||||
|  |   hiddenFooter?: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export type DialogEmits = { | ||||||
|  |   "update:modelValue": [val: boolean]; | ||||||
|  |   "update:fullScreen": [val: boolean]; | ||||||
|  |   confirm: []; | ||||||
|  |   cancel: []; | ||||||
|  | }; | ||||||
| @ -21,6 +21,7 @@ import "element-plus/dist/index.css"; | |||||||
| // 导入字体图标
 | // 导入字体图标
 | ||||||
| import "./assets/iconfont/iconfont.js"; | import "./assets/iconfont/iconfont.js"; | ||||||
| import "./assets/iconfont/iconfont.css"; | import "./assets/iconfont/iconfont.css"; | ||||||
|  | import "@/components/VDialog/dialog.css"; | ||||||
| 
 | 
 | ||||||
| const app = createApp(App); | const app = createApp(App); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -42,5 +42,5 @@ export type userType = { | |||||||
|   /** 字典ListMap 用于下拉框直接展示 */ |   /** 字典ListMap 用于下拉框直接展示 */ | ||||||
|   dictionaryList: Map<String, Array<DictionaryData>>; |   dictionaryList: Map<String, Array<DictionaryData>>; | ||||||
|   /** 字典MapMap 用于匹配值展示 */ |   /** 字典MapMap 用于匹配值展示 */ | ||||||
|   dictionaryMap: Map<String, Map<String, DictionaryData>>; |   dictionaryMap: Record<string, Record<string, DictionaryData>>; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -205,3 +205,25 @@ export const handleTree = ( | |||||||
|   } |   } | ||||||
|   return tree; |   return tree; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | export interface Tree { | ||||||
|  |   children?: this[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function toTree<T extends Tree>( | ||||||
|  |   src: T[], | ||||||
|  |   keyField: keyof T, | ||||||
|  |   parentField: keyof T | ||||||
|  | ): T[] { | ||||||
|  |   const map = new Map<unknown, T>(src.map(it => [it[keyField], it])); | ||||||
|  |   src.forEach(it => { | ||||||
|  |     if (map.has(it[parentField])) { | ||||||
|  |       const parent = map.get(it[parentField])!; | ||||||
|  |       if (!parent.children) { | ||||||
|  |         parent.children = []; | ||||||
|  |       } | ||||||
|  |       parent.children.push(it); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   return src.filter(it => !it[parentField]); | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref } from "vue"; | import { ref } from "vue"; | ||||||
| import { useLoginLogHook } from "./utils/hook"; | import { usePostHook } from "./utils/hook"; | ||||||
| import { PureTableBar } from "@/components/RePureTableBar"; | import { PureTableBar } from "@/components/RePureTableBar"; | ||||||
| import { useRenderIcon } from "@/components/ReIcon/src/hooks"; | import { useRenderIcon } from "@/components/ReIcon/src/hooks"; | ||||||
| 
 | 
 | ||||||
| @ -10,14 +10,17 @@ import Refresh from "@iconify-icons/ep/refresh"; | |||||||
| import { useUserStoreHook } from "@/store/modules/user"; | import { useUserStoreHook } from "@/store/modules/user"; | ||||||
| // TODO 这个导入声明好长  看看如何优化 | // TODO 这个导入声明好长  看看如何优化 | ||||||
| import { CommonUtils } from "@/utils/common"; | import { CommonUtils } from "@/utils/common"; | ||||||
|  | import PostFormModal from "@/views/system/post/post-form-modal.vue"; | ||||||
|  | import EditPen from "@iconify-icons/ep/edit-pen"; | ||||||
|  | import { PostPageResponse } from "@/api/system/post"; | ||||||
|  | import AddFill from "@iconify-icons/ri/add-circle-line"; | ||||||
| 
 | 
 | ||||||
| /** 组件name最好和菜单表中的router_name一致 */ | /** 组件name最好和菜单表中的router_name一致 */ | ||||||
| defineOptions({ | defineOptions({ | ||||||
|   name: "SystemOperationLog" |   name: "Post" | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const loginLogStatusList = | const loginLogStatusList = useUserStoreHook().dictionaryList["common.status"]; | ||||||
|   useUserStoreHook().dictionaryList["sysLoginLog.status"]; |  | ||||||
| 
 | 
 | ||||||
| const tableRef = ref(); | const tableRef = ref(); | ||||||
| 
 | 
 | ||||||
| @ -33,11 +36,21 @@ const { | |||||||
|   multipleSelection, |   multipleSelection, | ||||||
|   onSearch, |   onSearch, | ||||||
|   resetForm, |   resetForm, | ||||||
|  |   onSortChanged, | ||||||
|   exportAllExcel, |   exportAllExcel, | ||||||
|   getLoginLogList, |   getPostList, | ||||||
|   handleDelete, |   handleDelete, | ||||||
|   handleBulkDelete |   handleBulkDelete | ||||||
| } = useLoginLogHook(); | } = usePostHook(); | ||||||
|  | 
 | ||||||
|  | const opType = ref<"add" | "update">("add"); | ||||||
|  | const modalVisible = ref(false); | ||||||
|  | const opRow = ref<PostPageResponse>(); | ||||||
|  | function openDialog(type: "add" | "update", row?: PostPageResponse) { | ||||||
|  |   opType.value = type; | ||||||
|  |   opRow.value = row; | ||||||
|  |   modalVisible.value = true; | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
| @ -49,18 +62,18 @@ const { | |||||||
|       :model="searchFormParams" |       :model="searchFormParams" | ||||||
|       class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]" |       class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]" | ||||||
|     > |     > | ||||||
|       <el-form-item label="登录IP:" prop="ipAddress"> |       <el-form-item label="岗位编码" prop="postCode"> | ||||||
|         <el-input |         <el-input | ||||||
|           v-model="searchFormParams.ipAddress" |           v-model="searchFormParams.postCode" | ||||||
|           placeholder="请输入IP地址" |           placeholder="请输入岗位编码" | ||||||
|           clearable |           clearable | ||||||
|           class="!w-[200px]" |           class="!w-[200px]" | ||||||
|         /> |         /> | ||||||
|       </el-form-item> |       </el-form-item> | ||||||
|       <el-form-item label="用户名:" prop="username"> |       <el-form-item label="岗位名称" prop="postName"> | ||||||
|         <el-input |         <el-input | ||||||
|           v-model="searchFormParams.username" |           v-model="searchFormParams.postName" | ||||||
|           placeholder="请选择用户名称" |           placeholder="请选择岗位名称" | ||||||
|           clearable |           clearable | ||||||
|           class="!w-[200px]" |           class="!w-[200px]" | ||||||
|         /> |         /> | ||||||
| @ -81,11 +94,7 @@ const { | |||||||
|           /> |           /> | ||||||
|         </el-select> |         </el-select> | ||||||
|       </el-form-item> |       </el-form-item> | ||||||
|       <el-form-item> |       <el-form-item label="创建时间"> | ||||||
|         <label class="el-form-item__label is-required font-bold" |  | ||||||
|           >登录时间:</label |  | ||||||
|         > |  | ||||||
|         <!-- TODO 如何消除这个v-model的warning --> |  | ||||||
|         <el-date-picker |         <el-date-picker | ||||||
|           class="!w-[240px]" |           class="!w-[240px]" | ||||||
|           v-model="timeRange" |           v-model="timeRange" | ||||||
| @ -115,9 +124,16 @@ const { | |||||||
|     </el-form> |     </el-form> | ||||||
| 
 | 
 | ||||||
|     <!-- table bar 包裹  table --> |     <!-- table bar 包裹  table --> | ||||||
|     <PureTableBar title="登录日志列表" :columns="columns" @refresh="onSearch"> |     <PureTableBar title="岗位列表" :columns="columns" @refresh="onSearch"> | ||||||
|       <!-- 表格操作栏 --> |       <!-- 表格操作栏 --> | ||||||
|       <template #buttons> |       <template #buttons> | ||||||
|  |         <el-button | ||||||
|  |           type="primary" | ||||||
|  |           :icon="useRenderIcon(AddFill)" | ||||||
|  |           @click="openDialog('add')" | ||||||
|  |         > | ||||||
|  |           新增岗位 | ||||||
|  |         </el-button> | ||||||
|         <el-button |         <el-button | ||||||
|           type="danger" |           type="danger" | ||||||
|           :icon="useRenderIcon(Delete)" |           :icon="useRenderIcon(Delete)" | ||||||
| @ -127,7 +143,7 @@ const { | |||||||
|         </el-button> |         </el-button> | ||||||
|         <el-button |         <el-button | ||||||
|           type="primary" |           type="primary" | ||||||
|           @click="CommonUtils.exportExcel(columns, dataList, '登录日志列表')" |           @click="CommonUtils.exportExcel(columns, dataList, '岗位列表')" | ||||||
|           >单页导出</el-button |           >单页导出</el-button | ||||||
|         > |         > | ||||||
|         <el-button type="primary" @click="exportAllExcel">全部导出</el-button> |         <el-button type="primary" @click="exportAllExcel">全部导出</el-button> | ||||||
| @ -151,16 +167,26 @@ const { | |||||||
|             background: 'var(--el-table-row-hover-bg-color)', |             background: 'var(--el-table-row-hover-bg-color)', | ||||||
|             color: 'var(--el-text-color-primary)' |             color: 'var(--el-text-color-primary)' | ||||||
|           }" |           }" | ||||||
|           @page-size-change="getLoginLogList" |           @page-size-change="getPostList" | ||||||
|           @page-current-change="getLoginLogList" |           @page-current-change="getPostList" | ||||||
|           @sort-change="getLoginLogList" |           @sort-change="onSortChanged" | ||||||
|           @selection-change=" |           @selection-change=" | ||||||
|             rows => (multipleSelection = rows.map(item => item.logId)) |             rows => (multipleSelection = rows.map(item => item.postId)) | ||||||
|           " |           " | ||||||
|         > |         > | ||||||
|           <template #operation="{ row }"> |           <template #operation="{ row }"> | ||||||
|  |             <el-button | ||||||
|  |               class="reset-margin" | ||||||
|  |               link | ||||||
|  |               type="primary" | ||||||
|  |               :size="size" | ||||||
|  |               :icon="useRenderIcon(EditPen)" | ||||||
|  |               @click="openDialog('update', row)" | ||||||
|  |             > | ||||||
|  |               编辑 | ||||||
|  |             </el-button> | ||||||
|             <el-popconfirm |             <el-popconfirm | ||||||
|               :title="`是否确认删除编号为${row.logId}的这条日志`" |               :title="`是否确认删除编号为${row.postId}的这个岗位`" | ||||||
|               @confirm="handleDelete(row)" |               @confirm="handleDelete(row)" | ||||||
|             > |             > | ||||||
|               <template #reference> |               <template #reference> | ||||||
| @ -179,6 +205,13 @@ const { | |||||||
|         </pure-table> |         </pure-table> | ||||||
|       </template> |       </template> | ||||||
|     </PureTableBar> |     </PureTableBar> | ||||||
|  | 
 | ||||||
|  |     <post-form-modal | ||||||
|  |       v-model="modalVisible" | ||||||
|  |       :type="opType" | ||||||
|  |       :row="opRow" | ||||||
|  |       @success="onSearch" | ||||||
|  |     /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										131
									
								
								src/views/system/post/post-form-modal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/views/system/post/post-form-modal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import VDialog from "@/components/VDialog/VDialog.vue"; | ||||||
|  | import { computed, reactive, ref } from "vue"; | ||||||
|  | import { | ||||||
|  |   AddPostCommand, | ||||||
|  |   PostPageResponse, | ||||||
|  |   UpdatePostCommand, | ||||||
|  |   addPostApi, | ||||||
|  |   updatePostApi | ||||||
|  | } from "@/api/system/post"; | ||||||
|  | import { useUserStoreHook } from "@/store/modules/user"; | ||||||
|  | import { ElMessage, FormInstance, FormRules } from "element-plus"; | ||||||
|  | 
 | ||||||
|  | interface Props { | ||||||
|  |   type: "add" | "update"; | ||||||
|  |   modelValue: boolean; | ||||||
|  |   row?: PostPageResponse; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const props = defineProps<Props>(); | ||||||
|  | const emits = defineEmits<{ | ||||||
|  |   (e: "update:modelValue", v: boolean): void; | ||||||
|  |   (e: "success"): void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const visible = computed({ | ||||||
|  |   get: () => props.modelValue, | ||||||
|  |   set(v) { | ||||||
|  |     emits("update:modelValue", v); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const formData = reactive<AddPostCommand | UpdatePostCommand>({ | ||||||
|  |   postId: 0, | ||||||
|  |   postCode: "", | ||||||
|  |   postName: "", | ||||||
|  |   postSort: 1, | ||||||
|  |   remark: "", | ||||||
|  |   status: "" | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const statusList = useUserStoreHook().dictionaryMap["common.status"]; | ||||||
|  | 
 | ||||||
|  | const rules: FormRules = { | ||||||
|  |   postName: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       message: "岗位名称不能为空" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   postCode: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       message: "岗位编码不能为空" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   postSort: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       message: "岗位序号不能为空" | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | }; | ||||||
|  | const formRef = ref<FormInstance>(); | ||||||
|  | function handleOpened() { | ||||||
|  |   if (props.row) { | ||||||
|  |     Object.assign(formData, props.row); | ||||||
|  |   } else { | ||||||
|  |     formRef.value?.resetFields(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const loading = ref(false); | ||||||
|  | async function handleConfirm() { | ||||||
|  |   try { | ||||||
|  |     loading.value = true; | ||||||
|  |     if (props.type === "add") { | ||||||
|  |       await addPostApi(formData); | ||||||
|  |     } else if (props.type === "update") { | ||||||
|  |       await updatePostApi(formData as UpdatePostCommand); | ||||||
|  |     } | ||||||
|  |     ElMessage.info("提交成功"); | ||||||
|  |     visible.value = false; | ||||||
|  |     emits("success"); | ||||||
|  |   } catch (e) { | ||||||
|  |     console.error(e); | ||||||
|  |     ElMessage.error((e as Error)?.message || "提交失败"); | ||||||
|  |   } finally { | ||||||
|  |     loading.value = false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <v-dialog | ||||||
|  |     show-full-screen | ||||||
|  |     :fixed-body-height="false" | ||||||
|  |     use-body-scrolling | ||||||
|  |     :title="type === 'add' ? '新增岗位' : '更新岗位'" | ||||||
|  |     v-model="visible" | ||||||
|  |     :loading="loading" | ||||||
|  |     @confirm="handleConfirm" | ||||||
|  |     @cancel="visible = false" | ||||||
|  |     @opened="handleOpened" | ||||||
|  |   > | ||||||
|  |     <el-form :model="formData" label-width="120px" :rules="rules" ref="formRef"> | ||||||
|  |       <el-form-item prop="postName" label="岗位名称" required inline-message> | ||||||
|  |         <el-input v-model="formData.postName" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="postCode" label="岗位编码" required> | ||||||
|  |         <el-input v-model="formData.postCode" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="postSort" label="岗位顺序" required> | ||||||
|  |         <el-input-number :min="1" v-model="formData.postSort" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="status" label="岗位状态"> | ||||||
|  |         <el-radio-group v-model="formData.status"> | ||||||
|  |           <el-radio | ||||||
|  |             v-for="item in Object.keys(statusList)" | ||||||
|  |             :key="item" | ||||||
|  |             :label="statusList[item].value" | ||||||
|  |             >{{ statusList[item].label }}</el-radio | ||||||
|  |           > | ||||||
|  |         </el-radio-group> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="remark" label="备注" style="margin-bottom: 0"> | ||||||
|  |         <el-input type="textarea" v-model="formData.remark" /> | ||||||
|  |       </el-form-item> | ||||||
|  |     </el-form> | ||||||
|  |   </v-dialog> | ||||||
|  | </template> | ||||||
| @ -1,23 +1,22 @@ | |||||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||||
| import { message } from "@/utils/message"; | import { message } from "@/utils/message"; | ||||||
| import { ElMessageBox, Sort } from "element-plus"; | import { ElMessageBox, Sort } from "element-plus"; | ||||||
| import { | import { reactive, ref, onMounted, toRaw, computed } from "vue"; | ||||||
|   getLoginLogListApi, |  | ||||||
|   deleteLoginLogApi, |  | ||||||
|   exportLoginLogExcelApi, |  | ||||||
|   LoginLogQuery |  | ||||||
| } from "@/api/system/log"; |  | ||||||
| import { reactive, ref, onMounted, toRaw } from "vue"; |  | ||||||
| import { useUserStoreHook } from "@/store/modules/user"; | import { useUserStoreHook } from "@/store/modules/user"; | ||||||
| import { CommonUtils } from "@/utils/common"; | import { CommonUtils } from "@/utils/common"; | ||||||
| import { PaginationProps } from "@pureadmin/table"; | import { PaginationProps } from "@pureadmin/table"; | ||||||
|  | import { | ||||||
|  |   PostListCommand, | ||||||
|  |   getPostListApi, | ||||||
|  |   exportPostExcelApi, | ||||||
|  |   deletePostApi | ||||||
|  | } from "@/api/system/post"; | ||||||
| 
 | 
 | ||||||
| const loginLogStatusMap = | const statusMap = useUserStoreHook().dictionaryMap["common.status"]; | ||||||
|   useUserStoreHook().dictionaryMap["sysLoginLog.status"]; |  | ||||||
| 
 | 
 | ||||||
| export function useLoginLogHook() { | export function usePostHook() { | ||||||
|   const defaultSort: Sort = { |   const defaultSort: Sort = { | ||||||
|     prop: "loginTime", |     prop: "createTime", | ||||||
|     order: "descending" |     order: "descending" | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -28,20 +27,35 @@ export function useLoginLogHook() { | |||||||
|     background: true |     background: true | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const timeRange = ref([]); |   const timeRange = computed<[string, string] | null>({ | ||||||
|  |     get() { | ||||||
|  |       if (searchFormParams.beginTime && searchFormParams.endTime) { | ||||||
|  |         return [searchFormParams.beginTime, searchFormParams.endTime]; | ||||||
|  |       } else { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     set(v) { | ||||||
|  |       if (v?.length === 2) { | ||||||
|  |         searchFormParams.beginTime = v[0]; | ||||||
|  |         searchFormParams.endTime = v[1]; | ||||||
|  |       } else { | ||||||
|  |         searchFormParams.beginTime = undefined; | ||||||
|  |         searchFormParams.endTime = undefined; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
| 
 | 
 | ||||||
|   const searchFormParams = reactive<LoginLogQuery>({ |   const searchFormParams = reactive<PostListCommand>({ | ||||||
|     ipAddress: undefined, |     postCode: "", | ||||||
|     username: undefined, |     postName: "", | ||||||
|     status: undefined, |     status: undefined | ||||||
|     beginTime: undefined, |  | ||||||
|     endTime: undefined, |  | ||||||
|     timeRangeColumn: defaultSort.prop |  | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   const dataList = ref([]); |   const dataList = ref([]); | ||||||
|   const pageLoading = ref(true); |   const pageLoading = ref(true); | ||||||
|   const multipleSelection = ref([]); |   const multipleSelection = ref([]); | ||||||
|  |   const sortState = ref<Sort>(defaultSort); | ||||||
| 
 | 
 | ||||||
|   const columns: TableColumnList = [ |   const columns: TableColumnList = [ | ||||||
|     { |     { | ||||||
| @ -49,34 +63,23 @@ export function useLoginLogHook() { | |||||||
|       align: "left" |       align: "left" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "日志编号", |       label: "岗位编号", | ||||||
|       prop: "logId", |       prop: "postId", | ||||||
|       minWidth: 100 |       minWidth: 100 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "用户名", |       label: "岗位编码", | ||||||
|       prop: "username", |       prop: "postCode", | ||||||
|       minWidth: 120, |  | ||||||
|       sortable: "custom" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       label: "IP地址", |  | ||||||
|       prop: "ipAddress", |  | ||||||
|       minWidth: 120 |       minWidth: 120 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "登录地点", |       label: "岗位名称", | ||||||
|       prop: "loginLocation", |       prop: "postName", | ||||||
|       minWidth: 120 |       minWidth: 120 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "操作系统", |       label: "岗位排序", | ||||||
|       prop: "operationSystem", |       prop: "postSort", | ||||||
|       minWidth: 120 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       label: "浏览器", |  | ||||||
|       prop: "browser", |  | ||||||
|       minWidth: 120 |       minWidth: 120 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @ -86,26 +89,20 @@ export function useLoginLogHook() { | |||||||
|       cellRenderer: ({ row, props }) => ( |       cellRenderer: ({ row, props }) => ( | ||||||
|         <el-tag |         <el-tag | ||||||
|           size={props.size} |           size={props.size} | ||||||
|           type={loginLogStatusMap[row.status].cssTag} |           type={statusMap[row.status].cssTag} | ||||||
|           effect="plain" |           effect="plain" | ||||||
|         > |         > | ||||||
|           {loginLogStatusMap[row.status].label} |           {statusMap[row.status].label} | ||||||
|         </el-tag> |         </el-tag> | ||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "状态名", |       label: "创建时间", | ||||||
|       prop: "statusStr", |  | ||||||
|       minWidth: 120, |  | ||||||
|       hide: true |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       label: "登录时间", |  | ||||||
|       minWidth: 160, |       minWidth: 160, | ||||||
|       prop: "loginTime", |       prop: "createTime", | ||||||
|       sortable: "custom", |       sortable: "custom", | ||||||
|       formatter: ({ loginTime }) => |       formatter: ({ createTime }) => | ||||||
|         dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss") |         dayjs(createTime).format("YYYY-MM-DD HH:mm:ss") | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "操作", |       label: "操作", | ||||||
| @ -115,10 +112,15 @@ export function useLoginLogHook() { | |||||||
|     } |     } | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
|  |   function onSortChanged(sort: Sort) { | ||||||
|  |     sortState.value = sort; | ||||||
|  |     onSearch(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async function onSearch() { |   async function onSearch() { | ||||||
|     // 点击搜索的时候 需要重置分页
 |     // 点击搜索的时候 需要重置分页
 | ||||||
|     pagination.currentPage = 1; |     pagination.currentPage = 1; | ||||||
|     getLoginLogList(); |     getPostList(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function resetForm(formEl, tableRef) { |   function resetForm(formEl, tableRef) { | ||||||
| @ -130,7 +132,6 @@ export function useLoginLogHook() { | |||||||
|     searchFormParams.orderDirection = undefined; |     searchFormParams.orderDirection = undefined; | ||||||
|     // 清空时间查询  TODO  这块有点繁琐  有可以优化的地方吗?
 |     // 清空时间查询  TODO  这块有点繁琐  有可以优化的地方吗?
 | ||||||
|     // Form组件的resetFields方法无法清除datepicker里面的数据。
 |     // Form组件的resetFields方法无法清除datepicker里面的数据。
 | ||||||
|     timeRange.value = []; |  | ||||||
|     searchFormParams.beginTime = undefined; |     searchFormParams.beginTime = undefined; | ||||||
|     searchFormParams.endTime = undefined; |     searchFormParams.endTime = undefined; | ||||||
|     tableRef.getTableRef().clearSort(); |     tableRef.getTableRef().clearSort(); | ||||||
| @ -138,15 +139,14 @@ export function useLoginLogHook() { | |||||||
|     onSearch(); |     onSearch(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async function getLoginLogList(sort: Sort = defaultSort) { |   async function getPostList(sort: Sort = defaultSort) { | ||||||
|     pageLoading.value = true; |     pageLoading.value = true; | ||||||
|     if (sort != null) { |     if (sort != null) { | ||||||
|       CommonUtils.fillSortParams(searchFormParams, sort); |       CommonUtils.fillSortParams(searchFormParams, sort); | ||||||
|     } |     } | ||||||
|     CommonUtils.fillPaginationParams(searchFormParams, pagination); |     CommonUtils.fillPaginationParams(searchFormParams, pagination); | ||||||
|     CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value); |  | ||||||
| 
 | 
 | ||||||
|     const { data } = await getLoginLogListApi(toRaw(searchFormParams)).finally( |     const { data } = await getPostListApi(toRaw(searchFormParams)).finally( | ||||||
|       () => { |       () => { | ||||||
|         pageLoading.value = false; |         pageLoading.value = false; | ||||||
|       } |       } | ||||||
| @ -155,23 +155,23 @@ export function useLoginLogHook() { | |||||||
|     pagination.total = data.total; |     pagination.total = data.total; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async function exportAllExcel(sort: Sort = defaultSort) { |   async function exportAllExcel() { | ||||||
|     if (sort != null) { |     if (sortState.value != null) { | ||||||
|       CommonUtils.fillSortParams(searchFormParams, sort); |       CommonUtils.fillSortParams(searchFormParams, sortState.value); | ||||||
|     } |     } | ||||||
|     CommonUtils.fillPaginationParams(searchFormParams, pagination); |     CommonUtils.fillPaginationParams(searchFormParams, pagination); | ||||||
|     CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value); |     CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value); | ||||||
| 
 | 
 | ||||||
|     exportLoginLogExcelApi(toRaw(searchFormParams), "登录日志.xls"); |     exportPostExcelApi(toRaw(searchFormParams), "岗位数据.xls"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async function handleDelete(row) { |   async function handleDelete(row) { | ||||||
|     await deleteLoginLogApi([row.logId]).then(() => { |     await deletePostApi([row.logId]).then(() => { | ||||||
|       message(`您删除了操作编号为${row.logId}的这条数据`, { |       message(`您删除了操作编号为${row.logId}的这条数据`, { | ||||||
|         type: "success" |         type: "success" | ||||||
|       }); |       }); | ||||||
|       // 刷新列表
 |       // 刷新列表
 | ||||||
|       getLoginLogList(); |       getPostList(); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -193,12 +193,12 @@ export function useLoginLogHook() { | |||||||
|       } |       } | ||||||
|     ) |     ) | ||||||
|       .then(async () => { |       .then(async () => { | ||||||
|         await deleteLoginLogApi(multipleSelection.value).then(() => { |         await deletePostApi(multipleSelection.value).then(() => { | ||||||
|           message(`您删除了日志编号为[ ${multipleSelection.value} ]的数据`, { |           message(`您删除了日志编号为[ ${multipleSelection.value} ]的数据`, { | ||||||
|             type: "success" |             type: "success" | ||||||
|           }); |           }); | ||||||
|           // 刷新列表
 |           // 刷新列表
 | ||||||
|           getLoginLogList(); |           getPostList(); | ||||||
|         }); |         }); | ||||||
|       }) |       }) | ||||||
|       .catch(() => { |       .catch(() => { | ||||||
| @ -210,9 +210,7 @@ export function useLoginLogHook() { | |||||||
|       }); |       }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onMounted(() => { |   onMounted(getPostList); | ||||||
|     getLoginLogList(); |  | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     searchFormParams, |     searchFormParams, | ||||||
| @ -224,9 +222,10 @@ export function useLoginLogHook() { | |||||||
|     timeRange, |     timeRange, | ||||||
|     multipleSelection, |     multipleSelection, | ||||||
|     onSearch, |     onSearch, | ||||||
|  |     onSortChanged, | ||||||
|     exportAllExcel, |     exportAllExcel, | ||||||
|     // exportExcel,
 |     // exportExcel,
 | ||||||
|     getLoginLogList, |     getPostList, | ||||||
|     resetForm, |     resetForm, | ||||||
|     handleDelete, |     handleDelete, | ||||||
|     handleBulkDelete |     handleBulkDelete | ||||||
|  | |||||||
| @ -1,55 +0,0 @@ | |||||||
| <script setup lang="ts"> |  | ||||||
| import { ref } from "vue"; |  | ||||||
| import { formRules } from "./utils/rule"; |  | ||||||
| import { FormProps } from "./utils/types"; |  | ||||||
| 
 |  | ||||||
| const props = withDefaults(defineProps<FormProps>(), { |  | ||||||
|   formInline: () => ({ |  | ||||||
|     name: "", |  | ||||||
|     code: "", |  | ||||||
|     remark: "" |  | ||||||
|   }) |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const ruleFormRef = ref(); |  | ||||||
| const newFormInline = ref(props.formInline); |  | ||||||
| 
 |  | ||||||
| function getRef() { |  | ||||||
|   return ruleFormRef.value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| defineExpose({ getRef }); |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <template> |  | ||||||
|   <el-form |  | ||||||
|     ref="ruleFormRef" |  | ||||||
|     :model="newFormInline" |  | ||||||
|     :rules="formRules" |  | ||||||
|     label-width="82px" |  | ||||||
|   > |  | ||||||
|     <el-form-item label="角色名称" prop="name"> |  | ||||||
|       <el-input |  | ||||||
|         v-model="newFormInline.name" |  | ||||||
|         clearable |  | ||||||
|         placeholder="请输入角色名称" |  | ||||||
|       /> |  | ||||||
|     </el-form-item> |  | ||||||
| 
 |  | ||||||
|     <el-form-item label="角色标识" prop="code"> |  | ||||||
|       <el-input |  | ||||||
|         v-model="newFormInline.code" |  | ||||||
|         clearable |  | ||||||
|         placeholder="请输入角色标识" |  | ||||||
|       /> |  | ||||||
|     </el-form-item> |  | ||||||
| 
 |  | ||||||
|     <el-form-item label="备注"> |  | ||||||
|       <el-input |  | ||||||
|         v-model="newFormInline.remark" |  | ||||||
|         placeholder="请输入备注信息" |  | ||||||
|         type="textarea" |  | ||||||
|       /> |  | ||||||
|     </el-form-item> |  | ||||||
|   </el-form> |  | ||||||
| </template> |  | ||||||
| @ -4,17 +4,17 @@ import { useRole } from "./utils/hook"; | |||||||
| import { PureTableBar } from "@/components/RePureTableBar"; | import { PureTableBar } from "@/components/RePureTableBar"; | ||||||
| import { useRenderIcon } from "@/components/ReIcon/src/hooks"; | import { useRenderIcon } from "@/components/ReIcon/src/hooks"; | ||||||
| 
 | 
 | ||||||
| // import Database from "@iconify-icons/ri/database-2-line"; |  | ||||||
| // import More from "@iconify-icons/ep/more-filled"; |  | ||||||
| import Delete from "@iconify-icons/ep/delete"; | import Delete from "@iconify-icons/ep/delete"; | ||||||
| import EditPen from "@iconify-icons/ep/edit-pen"; | import EditPen from "@iconify-icons/ep/edit-pen"; | ||||||
| import Search from "@iconify-icons/ep/search"; | import Search from "@iconify-icons/ep/search"; | ||||||
| import Refresh from "@iconify-icons/ep/refresh"; | import Refresh from "@iconify-icons/ep/refresh"; | ||||||
| import Menu from "@iconify-icons/ep/menu"; |  | ||||||
| import AddFill from "@iconify-icons/ri/add-circle-line"; | import AddFill from "@iconify-icons/ri/add-circle-line"; | ||||||
|  | import { getRoleInfoApi, RoleDTO } from "@/api/system/role"; | ||||||
|  | import RoleFormModal from "@/views/system/role/role-form-modal.vue"; | ||||||
|  | import { ElMessage } from "element-plus"; | ||||||
| 
 | 
 | ||||||
| defineOptions({ | defineOptions({ | ||||||
|   name: "Role" |   name: "SystemRole" | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const formRef = ref(); | const formRef = ref(); | ||||||
| @ -24,19 +24,34 @@ const { | |||||||
|   columns, |   columns, | ||||||
|   dataList, |   dataList, | ||||||
|   pagination, |   pagination, | ||||||
|   // buttonClass, |  | ||||||
|   onSearch, |   onSearch, | ||||||
|   resetForm, |   resetForm, | ||||||
|   openDialog, |   menuTree, | ||||||
|   handleMenu, |   getMenuTree, | ||||||
|   handleDelete, |   handleDelete | ||||||
|   // handleDatabase, |  | ||||||
|   handleSizeChange, |  | ||||||
|   handleCurrentChange, |  | ||||||
|   handleSelectionChange |  | ||||||
| } = useRole(); | } = useRole(); | ||||||
| </script> |  | ||||||
| 
 | 
 | ||||||
|  | const opType = ref<"add" | "update">("add"); | ||||||
|  | const modalVisible = ref(false); | ||||||
|  | const opRow = ref<RoleDTO>(); | ||||||
|  | async function openDialog(type: "add" | "update", row?: RoleDTO) { | ||||||
|  |   debugger; | ||||||
|  |   try { | ||||||
|  |     await getMenuTree(); | ||||||
|  |     if (row) { | ||||||
|  |       const { data } = await getRoleInfoApi(row.roleId); | ||||||
|  |       row.selectedMenuList = data.selectedMenuList; | ||||||
|  |       row.selectedDeptList = data.selectedDeptList; | ||||||
|  |     } | ||||||
|  |   } catch (e) { | ||||||
|  |     console.error(e); | ||||||
|  |     ElMessage.error((e as Error)?.message || "加载菜单失败"); | ||||||
|  |   } | ||||||
|  |   opType.value = type; | ||||||
|  |   opRow.value = row; | ||||||
|  |   modalVisible.value = true; | ||||||
|  | } | ||||||
|  | </script> | ||||||
| <template> | <template> | ||||||
|   <div class="main"> |   <div class="main"> | ||||||
|     <el-form |     <el-form | ||||||
| @ -47,7 +62,7 @@ const { | |||||||
|     > |     > | ||||||
|       <el-form-item label="角色名称:" prop="name"> |       <el-form-item label="角色名称:" prop="name"> | ||||||
|         <el-input |         <el-input | ||||||
|           v-model="form.name" |           v-model="form.roleName" | ||||||
|           placeholder="请输入角色名称" |           placeholder="请输入角色名称" | ||||||
|           clearable |           clearable | ||||||
|           class="!w-[200px]" |           class="!w-[200px]" | ||||||
| @ -55,7 +70,7 @@ const { | |||||||
|       </el-form-item> |       </el-form-item> | ||||||
|       <el-form-item label="角色标识:" prop="code"> |       <el-form-item label="角色标识:" prop="code"> | ||||||
|         <el-input |         <el-input | ||||||
|           v-model="form.code" |           v-model="form.roleKey" | ||||||
|           placeholder="请输入角色标识" |           placeholder="请输入角色标识" | ||||||
|           clearable |           clearable | ||||||
|           class="!w-[180px]" |           class="!w-[180px]" | ||||||
| @ -96,7 +111,7 @@ const { | |||||||
|         <el-button |         <el-button | ||||||
|           type="primary" |           type="primary" | ||||||
|           :icon="useRenderIcon(AddFill)" |           :icon="useRenderIcon(AddFill)" | ||||||
|           @click="openDialog()" |           @click="openDialog('add')" | ||||||
|         > |         > | ||||||
|           新增角色 |           新增角色 | ||||||
|         </el-button> |         </el-button> | ||||||
| @ -129,22 +144,12 @@ const { | |||||||
|               type="primary" |               type="primary" | ||||||
|               :size="size" |               :size="size" | ||||||
|               :icon="useRenderIcon(EditPen)" |               :icon="useRenderIcon(EditPen)" | ||||||
|               @click="openDialog('编辑', row)" |               @click="openDialog('update', row)" | ||||||
|             > |             > | ||||||
|               修改 |               修改 | ||||||
|             </el-button> |             </el-button> | ||||||
|             <el-button |  | ||||||
|               class="reset-margin" |  | ||||||
|               link |  | ||||||
|               type="primary" |  | ||||||
|               :size="size" |  | ||||||
|               :icon="useRenderIcon(Menu)" |  | ||||||
|               @click="handleMenu" |  | ||||||
|             > |  | ||||||
|               菜单权限 |  | ||||||
|             </el-button> |  | ||||||
|             <el-popconfirm |             <el-popconfirm | ||||||
|               :title="`是否确认删除角色名称为${row.name}的这条数据`" |               :title="`是否确认删除角色名称为${row.roleName}的这条数据`" | ||||||
|               @confirm="handleDelete(row)" |               @confirm="handleDelete(row)" | ||||||
|             > |             > | ||||||
|               <template #reference> |               <template #reference> | ||||||
| @ -200,6 +205,13 @@ const { | |||||||
|         </pure-table> |         </pure-table> | ||||||
|       </template> |       </template> | ||||||
|     </PureTableBar> |     </PureTableBar> | ||||||
|  | 
 | ||||||
|  |     <role-form-modal | ||||||
|  |       v-model="modalVisible" | ||||||
|  |       :type="opType" | ||||||
|  |       :row="opRow" | ||||||
|  |       :menu-options="menuTree" | ||||||
|  |     /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										158
									
								
								src/views/system/role/role-form-modal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								src/views/system/role/role-form-modal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import VDialog from "@/components/VDialog/VDialog.vue"; | ||||||
|  | import { computed, reactive, ref } from "vue"; | ||||||
|  | import { useUserStoreHook } from "@/store/modules/user"; | ||||||
|  | import { ElMessage, FormInstance, FormRules } from "element-plus"; | ||||||
|  | import { | ||||||
|  |   AddRoleCommand, | ||||||
|  |   RoleDTO, | ||||||
|  |   UpdateRoleCommand, | ||||||
|  |   addRoleApi, | ||||||
|  |   updateRoleApi | ||||||
|  | } from "@/api/system/role"; | ||||||
|  | import { MenuDTO } from "@/api/system/menu"; | ||||||
|  | 
 | ||||||
|  | interface Props { | ||||||
|  |   type: "add" | "update"; | ||||||
|  |   modelValue: boolean; | ||||||
|  |   row?: RoleDTO; | ||||||
|  |   menuOptions: MenuDTO[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const props = defineProps<Props>(); | ||||||
|  | const emits = defineEmits<{ | ||||||
|  |   (e: "update:modelValue", v: boolean): void; | ||||||
|  |   (e: "success"): void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const visible = computed({ | ||||||
|  |   get: () => props.modelValue, | ||||||
|  |   set(v) { | ||||||
|  |     emits("update:modelValue", v); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const formData = reactive<AddRoleCommand | UpdateRoleCommand>({ | ||||||
|  |   roleId: 0, | ||||||
|  |   dataScope: "", | ||||||
|  |   menuIds: [], | ||||||
|  |   remark: "", | ||||||
|  |   roleKey: "", | ||||||
|  |   roleName: "", | ||||||
|  |   roleSort: 1, | ||||||
|  |   status: "" | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const statusList = useUserStoreHook().dictionaryMap["common.status"]; | ||||||
|  | 
 | ||||||
|  | const rules: FormRules = { | ||||||
|  |   roleName: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       message: "角色名称不能为空" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   roleKey: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       message: "权限标识不能为空" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   roleSort: [ | ||||||
|  |     { | ||||||
|  |       required: true, | ||||||
|  |       message: "角色序号不能为空" | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | }; | ||||||
|  | const formRef = ref<FormInstance>(); | ||||||
|  | function handleOpened() { | ||||||
|  |   console.log("opened", props.row); | ||||||
|  |   if (props.row) { | ||||||
|  |     Object.assign(formData, props.row); | ||||||
|  |     formData.menuIds = props.row.selectedMenuList; | ||||||
|  |   } else { | ||||||
|  |     formRef.value?.resetFields(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const treeRef = ref<InstanceType<typeof ElTree>>(); | ||||||
|  | function handleCheckChange() { | ||||||
|  |   formData.menuIds = treeRef.value.getCheckedKeys(false) as number[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const loading = ref(false); | ||||||
|  | async function handleConfirm() { | ||||||
|  |   try { | ||||||
|  |     loading.value = true; | ||||||
|  |     if (props.type === "add") { | ||||||
|  |       await addRoleApi(formData); | ||||||
|  |     } else if (props.type === "update") { | ||||||
|  |       await updateRoleApi(formData as UpdateRoleCommand); | ||||||
|  |     } | ||||||
|  |     ElMessage.info("提交成功"); | ||||||
|  |     visible.value = false; | ||||||
|  |     emits("success"); | ||||||
|  |   } catch (e) { | ||||||
|  |     console.error(e); | ||||||
|  |     ElMessage.error((e as Error)?.message || "提交失败"); | ||||||
|  |   } finally { | ||||||
|  |     loading.value = false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <v-dialog | ||||||
|  |     show-full-screen | ||||||
|  |     fixed-body-height | ||||||
|  |     use-body-scrolling | ||||||
|  |     :title="type === 'add' ? '新增角色' : '更新角色'" | ||||||
|  |     v-model="visible" | ||||||
|  |     :loading="loading" | ||||||
|  |     @confirm="handleConfirm" | ||||||
|  |     @cancel="visible = false" | ||||||
|  |     @opened="handleOpened" | ||||||
|  |   > | ||||||
|  |     <el-form :model="formData" label-width="120px" :rules="rules" ref="formRef"> | ||||||
|  |       <el-form-item prop="roleName" label="角色名称" required inline-message> | ||||||
|  |         <el-input v-model="formData.roleName" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="roleKey" label="权限字符" required> | ||||||
|  |         <el-input v-model="formData.roleKey" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="roleSort" label="角色顺序" required> | ||||||
|  |         <el-input-number :min="1" v-model="formData.roleSort" /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="status" label="角色状态"> | ||||||
|  |         <el-radio-group v-model="formData.status"> | ||||||
|  |           <el-radio | ||||||
|  |             v-for="item in Object.keys(statusList)" | ||||||
|  |             :key="item" | ||||||
|  |             :label="statusList[item].value" | ||||||
|  |             >{{ statusList[item].label }}</el-radio | ||||||
|  |           > | ||||||
|  |         </el-radio-group> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item label="菜单权限" prop="menuIds"> | ||||||
|  |         <el-tree | ||||||
|  |           ref="treeRef" | ||||||
|  |           :props="{ label: 'menuName', children: 'children' }" | ||||||
|  |           :data="props.menuOptions" | ||||||
|  |           node-key="id" | ||||||
|  |           check-strictly | ||||||
|  |           show-checkbox | ||||||
|  |           default-expand-all | ||||||
|  |           check-on-click-node | ||||||
|  |           :expand-on-click-node="false" | ||||||
|  |           :default-checked-keys="formData.menuIds" | ||||||
|  |           @check-change="handleCheckChange" | ||||||
|  |           style="width: 100%" | ||||||
|  |         /> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item prop="remark" label="备注" style="margin-bottom: 0"> | ||||||
|  |         <el-input type="textarea" v-model="formData.remark" /> | ||||||
|  |       </el-form-item> | ||||||
|  |     </el-form> | ||||||
|  |   </v-dialog> | ||||||
|  | </template> | ||||||
| @ -1,21 +1,24 @@ | |||||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||||
| import editForm from "../form.vue"; |  | ||||||
| import { message } from "@/utils/message"; | import { message } from "@/utils/message"; | ||||||
| import { getRoleList } from "@/api/system"; | import { | ||||||
| import { ElMessageBox } from "element-plus"; |   deleteRoleApi, | ||||||
|  |   getRoleListApi, | ||||||
|  |   RoleDTO, | ||||||
|  |   RoleQuery | ||||||
|  | } from "@/api/system/role"; | ||||||
|  | import { getMenuListApi, MenuDTO } from "@/api/system/menu"; | ||||||
|  | import { ElMessage, ElMessageBox } from "element-plus"; | ||||||
| import { usePublicHooks } from "../../hooks"; | import { usePublicHooks } from "../../hooks"; | ||||||
| import { addDialog } from "@/components/ReDialog"; |  | ||||||
| import { type FormItemProps } from "../utils/types"; |  | ||||||
| import { type PaginationProps } from "@pureadmin/table"; | import { type PaginationProps } from "@pureadmin/table"; | ||||||
| import { reactive, ref, onMounted, h, toRaw } from "vue"; | import { reactive, ref, onMounted, toRaw } from "vue"; | ||||||
|  | import { toTree } from "@/utils/tree"; | ||||||
| 
 | 
 | ||||||
| export function useRole() { | export function useRole() { | ||||||
|   const form = reactive({ |   const form = reactive<RoleQuery>({ | ||||||
|     name: "", |     roleKey: "", | ||||||
|     code: "", |     roleName: "", | ||||||
|     status: "" |     status: undefined | ||||||
|   }); |   }); | ||||||
|   const formRef = ref(); |  | ||||||
|   const dataList = ref([]); |   const dataList = ref([]); | ||||||
|   const loading = ref(true); |   const loading = ref(true); | ||||||
|   const switchLoadMap = ref({}); |   const switchLoadMap = ref({}); | ||||||
| @ -29,17 +32,17 @@ export function useRole() { | |||||||
|   const columns: TableColumnList = [ |   const columns: TableColumnList = [ | ||||||
|     { |     { | ||||||
|       label: "角色编号", |       label: "角色编号", | ||||||
|       prop: "id", |       prop: "roleId", | ||||||
|       minWidth: 100 |       minWidth: 100 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "角色名称", |       label: "角色名称", | ||||||
|       prop: "name", |       prop: "roleName", | ||||||
|       minWidth: 120 |       minWidth: 120 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: "角色标识", |       label: "角色标识", | ||||||
|       prop: "code", |       prop: "roleKey", | ||||||
|       minWidth: 150 |       minWidth: 150 | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @ -131,34 +134,33 @@ export function useRole() { | |||||||
|       }); |       }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function handleDelete(row) { |   async function handleDelete(row: RoleDTO) { | ||||||
|     message(`您删除了角色名称为${row.name}的这条数据`, { type: "success" }); |     try { | ||||||
|     onSearch(); |       loading.value = true; | ||||||
|   } |       await deleteRoleApi(row.roleId); | ||||||
| 
 |       message(`您删除了角色名称为${row.roleName}的这条数据`, { type: "info" }); | ||||||
|   function handleSizeChange(val: number) { |       onSearch(); | ||||||
|     console.log(`${val} items per page`); |     } catch (e) { | ||||||
|   } |       console.error(e); | ||||||
| 
 |       message((e as Error)?.message || "删除失败", { type: "error" }); | ||||||
|   function handleCurrentChange(val: number) { |     } finally { | ||||||
|     console.log(`current page: ${val}`); |       loading.value = false; | ||||||
|   } |     } | ||||||
| 
 |  | ||||||
|   function handleSelectionChange(val) { |  | ||||||
|     console.log("handleSelectionChange", val); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async function onSearch() { |   async function onSearch() { | ||||||
|     loading.value = true; |     try { | ||||||
|     const { data } = await getRoleList(toRaw(form)); |       loading.value = true; | ||||||
|     dataList.value = data.list; |       const { data } = await getRoleListApi(toRaw(form)); | ||||||
|     pagination.total = data.total; |       console.log("role list", data); | ||||||
|     pagination.pageSize = data.pageSize; |       dataList.value = data.rows; | ||||||
|     pagination.currentPage = data.currentPage; |       pagination.total = data.total; | ||||||
| 
 |     } catch (e) { | ||||||
|     setTimeout(() => { |       console.error(e); | ||||||
|  |       ElMessage.error((e as Error)?.message || "加载失败"); | ||||||
|  |     } finally { | ||||||
|       loading.value = false; |       loading.value = false; | ||||||
|     }, 500); |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const resetForm = formEl => { |   const resetForm = formEl => { | ||||||
| @ -167,59 +169,22 @@ export function useRole() { | |||||||
|     onSearch(); |     onSearch(); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   function openDialog(title = "新增", row?: FormItemProps) { |   const menuTree = ref<MenuDTO[]>([]); | ||||||
|     addDialog({ |  | ||||||
|       title: `${title}角色`, |  | ||||||
|       props: { |  | ||||||
|         formInline: { |  | ||||||
|           name: row?.name ?? "", |  | ||||||
|           code: row?.code ?? "", |  | ||||||
|           remark: row?.remark ?? "" |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       width: "40%", |  | ||||||
|       draggable: true, |  | ||||||
|       fullscreenIcon: true, |  | ||||||
|       closeOnClickModal: false, |  | ||||||
|       contentRenderer: () => h(editForm, { ref: formRef }), |  | ||||||
|       beforeSure: (done, { options }) => { |  | ||||||
|         const FormRef = formRef.value.getRef(); |  | ||||||
|         const curData = options.props.formInline as FormItemProps; |  | ||||||
|         function chores() { |  | ||||||
|           message(`您${title}了角色名称为${curData.name}的这条数据`, { |  | ||||||
|             type: "success" |  | ||||||
|           }); |  | ||||||
|           done(); // 关闭弹框
 |  | ||||||
|           onSearch(); // 刷新表格数据
 |  | ||||||
|         } |  | ||||||
|         FormRef.validate(valid => { |  | ||||||
|           if (valid) { |  | ||||||
|             console.log("curData", curData); |  | ||||||
|             // 表单规则校验通过
 |  | ||||||
|             if (title === "新增") { |  | ||||||
|               // 实际开发先调用新增接口,再进行下面操作
 |  | ||||||
|               chores(); |  | ||||||
|             } else { |  | ||||||
|               // 实际开发先调用编辑接口,再进行下面操作
 |  | ||||||
|               chores(); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** 菜单权限 */ |   /** 菜单权限 */ | ||||||
|   function handleMenu() { |   async function getMenuTree() { | ||||||
|     message("等菜单管理页面开发后完善"); |     if (menuTree.value?.length) { | ||||||
|  |       return menuTree.value; | ||||||
|  |     } | ||||||
|  |     const { data } = await getMenuListApi({ isButton: false }); | ||||||
|  |     console.log("menu data", data); | ||||||
|  |     menuTree.value = toTree(data, "id", "parentId"); | ||||||
|  |     return menuTree.value; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** 数据权限 可自行开发 */ |   /** 数据权限 可自行开发 */ | ||||||
|   // function handleDatabase() {}
 |   // function handleDatabase() {}
 | ||||||
| 
 | 
 | ||||||
|   onMounted(() => { |   onMounted(onSearch); | ||||||
|     onSearch(); |  | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     form, |     form, | ||||||
| @ -227,15 +192,10 @@ export function useRole() { | |||||||
|     columns, |     columns, | ||||||
|     dataList, |     dataList, | ||||||
|     pagination, |     pagination, | ||||||
|     // buttonClass,
 |  | ||||||
|     onSearch, |     onSearch, | ||||||
|     resetForm, |     resetForm, | ||||||
|     openDialog, |     menuTree, | ||||||
|     handleMenu, |     getMenuTree, | ||||||
|     handleDelete, |     handleDelete | ||||||
|     // handleDatabase,
 |  | ||||||
|     handleSizeChange, |  | ||||||
|     handleCurrentChange, |  | ||||||
|     handleSelectionChange |  | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,8 +0,0 @@ | |||||||
| import { reactive } from "vue"; |  | ||||||
| import type { FormRules } from "element-plus"; |  | ||||||
| 
 |  | ||||||
| /** 自定义表单规则校验 */ |  | ||||||
| export const formRules = reactive(<FormRules>{ |  | ||||||
|   name: [{ required: true, message: "角色名称为必填项", trigger: "blur" }], |  | ||||||
|   code: [{ required: true, message: "角色标识为必填项", trigger: "blur" }] |  | ||||||
| }); |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| // 虽然字段很少 但是抽离出来 后续有扩展字段需求就很方便了
 |  | ||||||
| 
 |  | ||||||
| interface FormItemProps { |  | ||||||
|   /** 角色名称 */ |  | ||||||
|   name: string; |  | ||||||
|   /** 角色编号 */ |  | ||||||
|   code: string; |  | ||||||
|   /** 备注 */ |  | ||||||
|   remark: string; |  | ||||||
| } |  | ||||||
| interface FormProps { |  | ||||||
|   formInline: FormItemProps; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export type { FormItemProps, FormProps }; |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user