mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +08:00 
			
		
		
		
	| @@ -61,8 +61,8 @@ public class MessageServiceImpl | |||||||
|     @Override |     @Override | ||||||
|     public PageDataVO<MessageVO> page(MessageQuery query, PageQuery pageQuery) { |     public PageDataVO<MessageVO> page(MessageQuery query, PageQuery pageQuery) { | ||||||
|         QueryWrapper<MessageDO> queryWrapper = QueryHelper.build(query); |         QueryWrapper<MessageDO> queryWrapper = QueryHelper.build(query); | ||||||
|         queryWrapper.apply(null != query.getUid(), "msgUser.user_id={0}", query.getUid()); |         queryWrapper.apply(null != query.getUid(), "msgUser.user_id={0}", query.getUid()) | ||||||
|         queryWrapper.apply(null != query.getReadStatus(), "msgUser.read_status={0}", query.getReadStatus()); |             .apply(null != query.getReadStatus(), "msgUser.read_status={0}", query.getReadStatus()); | ||||||
|         IPage<MessageVO> page = baseMapper.selectVoPage(pageQuery.toPage(), queryWrapper); |         IPage<MessageVO> page = baseMapper.selectVoPage(pageQuery.toPage(), queryWrapper); | ||||||
|         page.getRecords().forEach(this::fill); |         page.getRecords().forEach(this::fill); | ||||||
|         return PageDataVO.build(page); |         return PageDataVO.build(page); | ||||||
| @@ -71,8 +71,8 @@ public class MessageServiceImpl | |||||||
|     @Override |     @Override | ||||||
|     public List<MessageVO> list(MessageQuery query, SortQuery sortQuery) { |     public List<MessageVO> list(MessageQuery query, SortQuery sortQuery) { | ||||||
|         QueryWrapper<MessageDO> queryWrapper = QueryHelper.build(query); |         QueryWrapper<MessageDO> queryWrapper = QueryHelper.build(query); | ||||||
|         queryWrapper.apply("msgUser.user_id={0}", LoginHelper.getUserId()); |         queryWrapper.apply("msgUser.user_id={0}", LoginHelper.getUserId()).apply(null != query.getReadStatus(), | ||||||
|         queryWrapper.apply(null != query.getReadStatus(), "msgUser.read_status={0}", query.getReadStatus()); |             "msgUser.read_status={0}", query.getReadStatus()); | ||||||
|         // 设置排序 |         // 设置排序 | ||||||
|         this.sort(queryWrapper, sortQuery); |         this.sort(queryWrapper, sortQuery); | ||||||
|         return baseMapper.selectVoList(queryWrapper); |         return baseMapper.selectVoList(queryWrapper); | ||||||
| @@ -99,8 +99,8 @@ public class MessageServiceImpl | |||||||
|         messageUserService.add(messageId, userIdList); |         messageUserService.add(messageId, userIdList); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional(rollbackFor = Exception.class) |  | ||||||
|     @Override |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|     public void delete(List<Long> ids) { |     public void delete(List<Long> ids) { | ||||||
|         super.delete(ids); |         super.delete(ids); | ||||||
|         messageUserService.delete(ids); |         messageUserService.delete(ids); | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ | |||||||
|     "query-string": "^8.1.0", |     "query-string": "^8.1.0", | ||||||
|     "sortablejs": "^1.15.0", |     "sortablejs": "^1.15.0", | ||||||
|     "vue": "^3.3.4", |     "vue": "^3.3.4", | ||||||
|  |     "vue-cropper": "^1.0.9", | ||||||
|     "vue-echarts": "^6.6.1", |     "vue-echarts": "^6.6.1", | ||||||
|     "vue-i18n": "^9.5.0", |     "vue-i18n": "^9.5.0", | ||||||
|     "vue-json-pretty": "^2.2.4", |     "vue-json-pretty": "^2.2.4", | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								continew-admin-ui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								continew-admin-ui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @@ -58,6 +58,9 @@ dependencies: | |||||||
|   vue: |   vue: | ||||||
|     specifier: ^3.3.4 |     specifier: ^3.3.4 | ||||||
|     version: 3.3.4 |     version: 3.3.4 | ||||||
|  |   vue-cropper: | ||||||
|  |     specifier: ^1.0.9 | ||||||
|  |     version: 1.0.9 | ||||||
|   vue-echarts: |   vue-echarts: | ||||||
|     specifier: ^6.6.1 |     specifier: ^6.6.1 | ||||||
|     version: 6.6.1(echarts@5.4.3)(vue@3.3.4) |     version: 6.6.1(echarts@5.4.3)(vue@3.3.4) | ||||||
|   | |||||||
| @@ -12,6 +12,20 @@ export interface AvatarRes { | |||||||
|   avatar: string; |   avatar: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface cropperOptions { | ||||||
|  |   autoCrop: boolean; // 是否默认生成截图框 | ||||||
|  |   autoCropWidth: number; // 默认生成截图框宽度 | ||||||
|  |   autoCropHeight: number; // 默认生成截图框高度 | ||||||
|  |   canMove: boolean; // 上传图片是否可以移动  (默认:true) | ||||||
|  |   centerBox: boolean; // 截图框是否被限制在图片里面  (默认:false) | ||||||
|  |   full: boolean; // 是否输出原图比例的截图 选true生成的图片会非常大  (默认:false) | ||||||
|  |   fixed: boolean; // 是否开启截图框宽高固定比例  (默认:false) | ||||||
|  |   fixedBox: boolean; // 固定截图框大小 不允许改变 | ||||||
|  |   img: string | ArrayBuffer | null; // 裁剪图片的地址 | ||||||
|  |   outputSize: number; // 裁剪生成图片的质量  (默认:1) | ||||||
|  |   outputType: string; // 默认生成截图为PNG格式 | ||||||
|  | } | ||||||
|  |  | ||||||
| export function uploadAvatar(data: FormData) { | export function uploadAvatar(data: FormData) { | ||||||
|   return axios.post<AvatarRes>(`${BASE_URL}/avatar`, data); |   return axios.post<AvatarRes>(`${BASE_URL}/avatar`, data); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,8 +7,7 @@ | |||||||
|         :show-file-list="false" |         :show-file-list="false" | ||||||
|         list-type="picture-card" |         list-type="picture-card" | ||||||
|         :show-upload-button="true" |         :show-upload-button="true" | ||||||
|         :custom-request="handleUpload" |         :on-before-upload="handleBeforeUpload" | ||||||
|         @change="handleAvatarChange" |  | ||||||
|       > |       > | ||||||
|         <template #upload-button> |         <template #upload-button> | ||||||
|           <a-avatar :size="100" class="info-avatar"> |           <a-avatar :size="100" class="info-avatar"> | ||||||
| @@ -22,6 +21,56 @@ | |||||||
|         </template> |         </template> | ||||||
|       </a-upload> |       </a-upload> | ||||||
|  |  | ||||||
|  |       <div class="main"> | ||||||
|  |         <a-modal | ||||||
|  |           :visible="cropperVisible" | ||||||
|  |           width="40%" | ||||||
|  |           :footer="false" | ||||||
|  |           unmount-on-close | ||||||
|  |           render-to-body | ||||||
|  |           @cancel="handleCropperCancel" | ||||||
|  |         > | ||||||
|  |           <a-row> | ||||||
|  |             <a-col :span="14"> | ||||||
|  |               <div style="width: 370px; height: 370px"> | ||||||
|  |                 <!-- 头像裁剪框 --> | ||||||
|  |                 <vue-cropper | ||||||
|  |                   ref="cropper" | ||||||
|  |                   :info="true" | ||||||
|  |                   :img="options.img" | ||||||
|  |                   :full="options.full" | ||||||
|  |                   :fixed="options.fixed" | ||||||
|  |                   :fixed-box="options.fixedBox" | ||||||
|  |                   :can-move="options.canMove" | ||||||
|  |                   :center-box="options.centerBox" | ||||||
|  |                   :auto-crop="options.autoCrop" | ||||||
|  |                   :auto-crop-width="options.autoCropWidth" | ||||||
|  |                   :auto-crop-height="options.autoCropHeight" | ||||||
|  |                   :output-type="options.outputType" | ||||||
|  |                   :output-size="options.outputSize" | ||||||
|  |                   @realTime="realTime" | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </a-col> | ||||||
|  |             <a-col :span="6"> | ||||||
|  |               <!-- 实时预览 --> | ||||||
|  |               <div :style="previewStyle"> | ||||||
|  |                 <div :style="previews.div"> | ||||||
|  |                   <img :src="previews.url" :style="previews.img" alt="" /> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             </a-col> | ||||||
|  |           </a-row> | ||||||
|  |           <br /> | ||||||
|  |           <a-space> | ||||||
|  |             <a-button type="primary" @click="handleUpload">提交</a-button> | ||||||
|  |             <a-button type="outline" @click="handleCropperCancel" | ||||||
|  |               >取消</a-button | ||||||
|  |             > | ||||||
|  |           </a-space> | ||||||
|  |         </a-modal> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|       <a-descriptions |       <a-descriptions | ||||||
|         :column="2" |         :column="2" | ||||||
|         :label-style="{ |         :label-style="{ | ||||||
| @@ -70,15 +119,22 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
|   import { getCurrentInstance, ref } from 'vue'; |   import { reactive, ref, getCurrentInstance } from 'vue'; | ||||||
|   import { FileItem, RequestOption } from '@arco-design/web-vue'; |   import { FileItem } from '@arco-design/web-vue'; | ||||||
|   import { uploadAvatar } from '@/api/system/user-center'; |   import { uploadAvatar, cropperOptions } from '@/api/system/user-center'; | ||||||
|   import { useUserStore } from '@/store'; |  | ||||||
|   import getAvatar from '@/utils/avatar'; |   import getAvatar from '@/utils/avatar'; | ||||||
|  |   import { useUserStore } from '@/store'; | ||||||
|  |   import { VueCropper } from 'vue-cropper'; | ||||||
|  |   import 'vue-cropper/dist/index.css'; | ||||||
|  |  | ||||||
|  |   const fileRef = ref(reactive({ name: 'avatar.png' })); | ||||||
|  |   const previews: any = ref({}); | ||||||
|  |   const previewStyle: any = ref({}); | ||||||
|  |   const cropperVisible = ref(false); | ||||||
|  |   const cropper = ref(); | ||||||
|   const { proxy } = getCurrentInstance() as any; |   const { proxy } = getCurrentInstance() as any; | ||||||
|  |  | ||||||
|   const userStore = useUserStore(); |   const userStore = useUserStore(); | ||||||
|  |  | ||||||
|   const avatar = { |   const avatar = { | ||||||
|     uid: '-2', |     uid: '-2', | ||||||
|     name: 'avatar.png', |     name: 'avatar.png', | ||||||
| @@ -86,52 +142,75 @@ | |||||||
|   }; |   }; | ||||||
|   const avatarList = ref<FileItem[]>([avatar]); |   const avatarList = ref<FileItem[]>([avatar]); | ||||||
|  |  | ||||||
|  |   const options: cropperOptions = reactive({ | ||||||
|  |     autoCrop: true, | ||||||
|  |     autoCropWidth: 200, | ||||||
|  |     autoCropHeight: 200, | ||||||
|  |     canMove: true, | ||||||
|  |     centerBox: true, | ||||||
|  |     full: false, | ||||||
|  |     fixed: false, | ||||||
|  |     fixedBox: false, | ||||||
|  |     img: '', | ||||||
|  |     outputSize: 1, | ||||||
|  |     outputType: 'png', | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * 上传头像 |    * 上传前弹出裁剪框 | ||||||
|    * |    * | ||||||
|    * @param options 选项 |    * @param file 头像 | ||||||
|    */ |    */ | ||||||
|   const handleUpload = (options: RequestOption) => { |   const handleBeforeUpload = (file: File): boolean => { | ||||||
|     const controller = new AbortController(); |     fileRef.value = file; | ||||||
|     (async function requestWrap() { |     const reader = new FileReader(); | ||||||
|       const { |     reader.readAsDataURL(file); | ||||||
|         onProgress, |     reader.onload = () => { | ||||||
|         onError, |       options.img = reader.result; | ||||||
|         onSuccess, |  | ||||||
|         fileItem, |  | ||||||
|         name = 'avatarFile', |  | ||||||
|       } = options; |  | ||||||
|       onProgress(20); |  | ||||||
|       const formData = new FormData(); |  | ||||||
|       formData.append(name as string, fileItem.file as Blob); |  | ||||||
|       uploadAvatar(formData) |  | ||||||
|         .then((res) => { |  | ||||||
|           onSuccess(res); |  | ||||||
|           userStore.avatar = res.data.avatar; |  | ||||||
|           proxy.$message.success(res.msg); |  | ||||||
|         }) |  | ||||||
|         .catch((error) => { |  | ||||||
|           onError(error); |  | ||||||
|         }); |  | ||||||
|     })(); |  | ||||||
|     return { |  | ||||||
|       abort() { |  | ||||||
|         controller.abort(); |  | ||||||
|       }, |  | ||||||
|     }; |     }; | ||||||
|  |     cropperVisible.value = true; | ||||||
|  |     return false; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * 切换头像 |    * 关闭裁剪框 | ||||||
|    * |  | ||||||
|    * @param fileItemList 文件列表 |  | ||||||
|    * @param currentFile 当前文件 |  | ||||||
|    */ |    */ | ||||||
|   const handleAvatarChange = ( |   const handleCropperCancel = () => { | ||||||
|     fileItemList: FileItem[], |     fileRef.value = { name: '' }; | ||||||
|     currentFile: FileItem |     options.img = ''; | ||||||
|   ) => { |     cropperVisible.value = false; | ||||||
|     avatarList.value = [currentFile]; |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 上传头像 | ||||||
|  |    */ | ||||||
|  |   const handleUpload = () => { | ||||||
|  |     cropper.value.getCropBlob((data: string | Blob) => { | ||||||
|  |       const formData = new FormData(); | ||||||
|  |       formData.append('avatarFile', data, fileRef.value?.name); | ||||||
|  |       uploadAvatar(formData).then((res) => { | ||||||
|  |         userStore.avatar = res.data.avatar; | ||||||
|  |         avatarList.value[0].url = getAvatar(res.data.avatar, undefined); | ||||||
|  |         proxy.$message.success(res.msg); | ||||||
|  |         handleCropperCancel(); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 实时预览 | ||||||
|  |    * @param data data | ||||||
|  |    */ | ||||||
|  |   const realTime = (data: any) => { | ||||||
|  |     previewStyle.value = { | ||||||
|  |       width: `${data.w}px`, | ||||||
|  |       height: `${data.h}px`, | ||||||
|  |       overflow: 'hidden', | ||||||
|  |       margin: '0', | ||||||
|  |       zoom: 0.8, | ||||||
|  |       borderRadius: '50%', | ||||||
|  |     }; | ||||||
|  |     previews.value = data; | ||||||
|   }; |   }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| @@ -152,4 +231,8 @@ | |||||||
|       font-size: 14px; |       font-size: 14px; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   .main { | ||||||
|  |     position: relative; | ||||||
|  |   } | ||||||
| </style> | </style> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Gitee
						Gitee