feat: 🚀 用户修改头像

This commit is contained in:
Bunny 2024-10-04 23:34:31 +08:00
parent e9112177d4
commit 6e7bf90315
7 changed files with 62 additions and 198 deletions

View File

@ -6,11 +6,6 @@ export const getRouterAsync = () => {
return http.request<BaseResult<any>>('get', 'router/getRouterAsync'); return http.request<BaseResult<any>>('get', 'router/getRouterAsync');
}; };
/** 图标管理-获取系统图标 */
export const getMenuIconList = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `menuIcon/getMenuIconList/${data.page}/${data.limit}`, { data });
};
/** 菜单管理-列表 */ /** 菜单管理-列表 */
export const getMenusList = (data?: any) => { export const getMenusList = (data?: any) => {
return http.request<BaseResult<ResultTable>>('get', `router/getMenusList`, { params: data }); return http.request<BaseResult<ResultTable>>('get', `router/getMenusList`, { params: data });
@ -32,67 +27,6 @@ export const deletedMenuByIds = (data?: any) => {
}; };
/** 上传文件 */ /** 上传文件 */
export const fetchUploadFIle = (data: any) => { export const fetchUploadFile = (data: any) => {
return http.post<BaseResult<any>>('/files/upload', { data }, { headers: { 'Content-Type': 'multipart/form-data' } }); return http.post<BaseResult<any>>('/files/upload', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
}; };
// ------------未确认------------
/** 获取系统管理-用户管理列表 */
export const getUserList = (data?: object) => {
return http.request<BaseResult<ResultTable>>('post', '/user', { data });
};
/** 系统管理-用户管理-获取所有角色列表 */
export const getAllRoleList = () => {
return http.request<any>('get', '/list-all-role');
};
/** 系统管理-用户管理-根据userId获取对应角色id列表userId用户id */
export const getRoleIds = (data?: object) => {
return http.request<any>('post', '/list-role-ids', { data });
};
/** 获取系统管理-角色管理列表 */
export const getRoleList = (data?: object) => {
return http.request<ResultTable>('post', '/role', { data });
};
/** 获取系统管理-部门管理列表 */
export const getDeptList = (data?: object) => {
return http.request<any>('post', '/dept', { data });
};
/** 获取系统监控-在线用户列表 */
export const getOnlineLogsList = (data?: object) => {
return http.request<ResultTable>('post', '/online-logs', { data });
};
/** 获取系统监控-登录日志列表 */
export const getLoginLogsList = (data?: object) => {
return http.request<ResultTable>('post', '/login-logs', { data });
};
/** 获取系统监控-操作日志列表 */
export const getOperationLogsList = (data?: object) => {
return http.request<ResultTable>('post', '/operation-logs', { data });
};
/** 获取系统监控-系统日志列表 */
export const getSystemLogsList = (data?: object) => {
return http.request<ResultTable>('post', '/system-logs', { data });
};
/** 获取系统监控-系统日志-根据 id 查日志详情 */
export const getSystemLogsDetail = (data?: object) => {
return http.request<any>('post', '/system-logs-detail', { data });
};
/** 获取角色管理-权限-菜单权限 */
export const getRoleMenu = (data?: object) => {
return http.request<any>('post', '/role-menu', { data });
};
/** 获取角色管理-权限-菜单权限-根据角色 id 查对应菜单 */
export const getRoleMenuIds = (data?: object) => {
return http.request<any>('post', '/role-menu-ids', { data });
};

View File

@ -71,3 +71,11 @@ export const fetchGetUserinfoById = (data?: object) => {
export const fetchUpdateUserPasswordByAdmin = (data: any) => { export const fetchUpdateUserPasswordByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/updateUserPasswordByAdmin', { data }); return http.request<BaseResult<UserResult>>('put', 'user/updateUserPasswordByAdmin', { data });
}; };
/**
*
* @param data
*/
export const fetchUploadAvatarByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/uploadAvatarByAdmin', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};

View File

@ -2,12 +2,12 @@ import './circled.css';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import { ElUpload } from 'element-plus'; import { ElUpload } from 'element-plus';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import { computed, defineComponent, onMounted, onUnmounted, type PropType, ref, unref } from 'vue';
import { useEventListener } from '@vueuse/core'; import { useEventListener } from '@vueuse/core';
import { longpress } from '@/directives/longpress'; import { longpress } from '@/directives/longpress';
import { useTippy, directive as tippy } from 'vue-tippy'; import { directive as tippy, useTippy } from 'vue-tippy';
import { type PropType, ref, unref, computed, onMounted, onUnmounted, defineComponent } from 'vue'; import { debounce, delay, downloadByBase64, isArray, useResizeObserver } from '@pureadmin/utils';
import { delay, debounce, isArray, downloadByBase64, useResizeObserver } from '@pureadmin/utils'; import { ArrowDown, ArrowH, ArrowLeft, ArrowRight, ArrowUp, ArrowV, ChangeIcon, DownloadIcon, Reload, RotateLeft, RotateRight, SearchMinus, SearchPlus, Upload } from './svg';
import { Reload, Upload, ArrowH, ArrowV, ArrowUp, ArrowDown, ArrowLeft, ChangeIcon, ArrowRight, RotateLeft, SearchPlus, RotateRight, SearchMinus, DownloadIcon } from './svg';
type Options = Cropper.Options; type Options = Cropper.Options;
@ -206,57 +206,20 @@ export default defineComponent({
return () => ( return () => (
<div class='flex flex-wrap w-[60px] justify-between'> <div class='flex flex-wrap w-[60px] justify-between'>
<ElUpload accept='image/*' show-file-list={false} before-upload={beforeUpload}> <ElUpload accept='image/*' show-file-list={false} before-upload={beforeUpload}>
<Upload <Upload class={iconClass.value} v-tippy={{ content: '上传', placement: 'left-start' }} />
class={iconClass.value}
v-tippy={{
content: '上传',
placement: 'left-start',
}}
/>
</ElUpload> </ElUpload>
<DownloadIcon <DownloadIcon class={iconClass.value} v-tippy={{ content: '下载', placement: 'right-start' }} onClick={() => downloadByBase64(imgBase64.value, 'cropping.png')} />
class={iconClass.value}
v-tippy={{
content: '下载',
placement: 'right-start',
}}
onClick={() => downloadByBase64(imgBase64.value, 'cropping.png')}
/>
<ChangeIcon <ChangeIcon
class={iconClass.value} class={iconClass.value}
v-tippy={{ v-tippy={{ content: '圆形、矩形裁剪', placement: 'left-start' }}
content: '圆形、矩形裁剪',
placement: 'left-start',
}}
onClick={() => { onClick={() => {
inCircled.value = !inCircled.value; inCircled.value = !inCircled.value;
realTimeCroppered(); realTimeCroppered();
}} }}
/> />
<Reload <Reload class={iconClass.value} v-tippy={{ content: '重置', placement: 'right-start' }} onClick={() => handCropper('reset')} />
class={iconClass.value} <ArrowUp class={iconClass.value} v-tippy={{ content: '上移(可长按)', placement: 'left-start' }} v-longpress={[() => handCropper('move', [0, -10]), '0:100']} />
v-tippy={{ <ArrowDown class={iconClass.value} v-tippy={{ content: '下移(可长按)', placement: 'right-start' }} v-longpress={[() => handCropper('move', [0, 10]), '0:100']} />
content: '重置',
placement: 'right-start',
}}
onClick={() => handCropper('reset')}
/>
<ArrowUp
class={iconClass.value}
v-tippy={{
content: '上移(可长按)',
placement: 'left-start',
}}
v-longpress={[() => handCropper('move', [0, -10]), '0:100']}
/>
<ArrowDown
class={iconClass.value}
v-tippy={{
content: '下移(可长按)',
placement: 'right-start',
}}
v-longpress={[() => handCropper('move', [0, 10]), '0:100']}
/>
<ArrowLeft <ArrowLeft
class={iconClass.value} class={iconClass.value}
v-tippy={{ v-tippy={{
@ -265,62 +228,13 @@ export default defineComponent({
}} }}
v-longpress={[() => handCropper('move', [-10, 0]), '0:100']} v-longpress={[() => handCropper('move', [-10, 0]), '0:100']}
/> />
<ArrowRight <ArrowRight class={iconClass.value} v-tippy={{ content: '右移(可长按)', placement: 'right-start' }} v-longpress={[() => handCropper('move', [10, 0]), '0:100']} />
class={iconClass.value} <ArrowH class={iconClass.value} v-tippy={{ content: '水平翻转', placement: 'left-start' }} onClick={() => handCropper('scaleX', -1)} />
v-tippy={{ <ArrowV class={iconClass.value} v-tippy={{ content: '垂直翻转', placement: 'right-start' }} onClick={() => handCropper('scaleY', -1)} />
content: '右移(可长按)', <RotateLeft class={iconClass.value} v-tippy={{ content: '逆时针旋转', placement: 'left-start' }} onClick={() => handCropper('rotate', -45)} />
placement: 'right-start', <RotateRight class={iconClass.value} v-tippy={{ content: '顺时针旋转', placement: 'right-start' }} onClick={() => handCropper('rotate', 45)} />
}} <SearchPlus class={iconClass.value} v-tippy={{ content: '放大(可长按)', placement: 'left-start' }} v-longpress={[() => handCropper('zoom', 0.1), '0:100']} />
v-longpress={[() => handCropper('move', [10, 0]), '0:100']} <SearchMinus class={iconClass.value} v-tippy={{ content: '缩小(可长按)', placement: 'right-start' }} v-longpress={[() => handCropper('zoom', -0.1), '0:100']} />
/>
<ArrowH
class={iconClass.value}
v-tippy={{
content: '水平翻转',
placement: 'left-start',
}}
onClick={() => handCropper('scaleX', -1)}
/>
<ArrowV
class={iconClass.value}
v-tippy={{
content: '垂直翻转',
placement: 'right-start',
}}
onClick={() => handCropper('scaleY', -1)}
/>
<RotateLeft
class={iconClass.value}
v-tippy={{
content: '逆时针旋转',
placement: 'left-start',
}}
onClick={() => handCropper('rotate', -45)}
/>
<RotateRight
class={iconClass.value}
v-tippy={{
content: '顺时针旋转',
placement: 'right-start',
}}
onClick={() => handCropper('rotate', 45)}
/>
<SearchPlus
class={iconClass.value}
v-tippy={{
content: '放大(可长按)',
placement: 'left-start',
}}
v-longpress={[() => handCropper('zoom', 0.1), '0:100']}
/>
<SearchMinus
class={iconClass.value}
v-tippy={{
content: '缩小(可长按)',
placement: 'right-start',
}}
v-longpress={[() => handCropper('zoom', -0.1), '0:100']}
/>
</div> </div>
); );
}, },

View File

@ -17,7 +17,7 @@ import { Plus } from '@element-plus/icons-vue';
import { ElMessage, UploadRawFile, UploadRequestOptions } from 'element-plus'; import { ElMessage, UploadRawFile, UploadRequestOptions } from 'element-plus';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { SystemEnum } from '@/enums/upload'; import { SystemEnum } from '@/enums/upload';
import { fetchUploadFIle } from '@/api/v1/system'; import { fetchUploadFile } from '@/api/v1/system';
const props = defineProps({ const props = defineProps({
imageUrl: String, imageUrl: String,
@ -38,7 +38,7 @@ const onUpload = async (options: UploadRequestOptions) => {
const data = { file, type }; const data = { file, type };
// //
const result: any = await fetchUploadFIle(data); const result: any = await fetchUploadFile(data);
imageSrc.value = result.data.url; imageSrc.value = result.data.url;
emits('uploadCallback', result); emits('uploadCallback', result);
}; };
@ -85,16 +85,16 @@ onMounted(() => {
}); });
</script> </script>
<style> <style lang="scss" scoped>
.el-upload { //.el-upload {
width: 128px; // width: 128px;
height: 128px; // height: 128px;
} //}
//
.el-upload-dragger { //.el-upload-dragger {
display: flex; // display: flex;
align-items: center; // align-items: center;
justify-content: center; // justify-content: center;
height: 100%; // height: 100%;
} //}
</style> </style>

View File

@ -6,7 +6,6 @@ import { FormProps } from '@/views/system/adminUser/utils/types';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
import ReCol from '@/components/MyCol'; import ReCol from '@/components/MyCol';
import { sexConstant } from '@/enums/baseConstant'; import { sexConstant } from '@/enums/baseConstant';
import UploadDialogImage from '@/components/Upload/UploadDialogImage.vue';
const props = withDefaults(defineProps<FormProps>(), { const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({ formInline: () => ({
@ -93,13 +92,6 @@ defineExpose({ formRef });
</el-form-item> </el-form-item>
</re-col> </re-col>
<!-- 头像 -->
<re-col :sm="24" :value="24" :xs="24">
<el-form-item :label="$t('adminUser_avatar')" prop="avatar">
<UploadDialogImage :image-url="form.avatar" type="avatar" @uploadCallback="onUploadCallback" />
</el-form-item>
</re-col>
<!-- 用户简介 --> <!-- 用户简介 -->
<re-col :sm="24" :value="24" :xs="24"> <re-col :sm="24" :value="24" :xs="24">
<el-form-item :label="$t('adminUser_summary')" prop="summary"> <el-form-item :label="$t('adminUser_summary')" prop="summary">

View File

@ -106,7 +106,7 @@ onMounted(() => {
<PureTableBar :columns="columns" title="用户信息" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch"> <PureTableBar :columns="columns" title="用户信息" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
<template #buttons> <template #buttons>
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> 添加用户信息</el-button> <el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> {{ $t('add_new') + $t('adminUser') }} </el-button>
</template> </template>
<template v-slot="{ size, dynamicColumns }"> <template v-slot="{ size, dynamicColumns }">

View File

@ -2,7 +2,7 @@ import { addDialog } from '@/components/BaseDialog/index';
import AdminUserDialog from '@/views/system/adminUser/admin-user-dialog.vue'; import AdminUserDialog from '@/views/system/adminUser/admin-user-dialog.vue';
import { useAdminUserStore } from '@/store/system/adminUser.ts'; import { useAdminUserStore } from '@/store/system/adminUser.ts';
import { h, reactive, ref } from 'vue'; import { h, reactive, ref } from 'vue';
import { messageBox } from '@/utils/message'; import { message, messageBox } from '@/utils/message';
import type { FormItemProps } from '@/views/system/adminUser/utils/types'; import type { FormItemProps } from '@/views/system/adminUser/utils/types';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
import { isAddUserinfo } from '@/views/system/adminUser/utils/columns'; import { isAddUserinfo } from '@/views/system/adminUser/utils/columns';
@ -11,6 +11,9 @@ import ResetPasswordDialog from '@/views/system/adminUser/reset-passwords.vue';
import { deviceDetection } from '@pureadmin/utils'; import { deviceDetection } from '@pureadmin/utils';
import CropperPreview from '@/components/CropperPreview'; import CropperPreview from '@/components/CropperPreview';
import AssignUserToRole from '@/views/system/adminUser/assign-user-to-role.vue'; import AssignUserToRole from '@/views/system/adminUser/assign-user-to-role.vue';
import { fetchUploadFile } from '@/api/v1/system';
import userAvatar from '@/assets/user.jpg';
import { fetchUploadAvatarByAdmin } from '@/api/v1/user';
export const formRef = ref(); export const formRef = ref();
const cropRef = ref(); const cropRef = ref();
@ -156,7 +159,10 @@ export const updateUserStatus = async (row: any) => {
await onSearch(); await onSearch();
}; };
/* 上传头像 */ /**
* *
* @param row
*/
export const onUploadAvatar = (row: any) => { export const onUploadAvatar = (row: any) => {
addDialog({ addDialog({
title: '裁剪、上传头像', title: '裁剪、上传头像',
@ -170,8 +176,18 @@ export const onUploadAvatar = (row: any) => {
onCropper: info => (avatarInfo.value = info), onCropper: info => (avatarInfo.value = info),
}), }),
beforeSure: async done => { beforeSure: async done => {
console.log('裁剪后的图片信息:', avatarInfo.value); // 上传头像
// 根据实际业务使用avatarInfo.value和row里的某些字段去调用上传头像接口即可 const blob = avatarInfo.value.blob;
const uploadData = { file: blob, type: 'avatar' };
let result = await fetchUploadFile(uploadData);
if (result.code !== 200) return;
// 修改头像
const data = { userId: row.id, avatar: result.data.filepath };
result = await fetchUploadAvatarByAdmin(data);
if (result.code !== 200) return;
message(result.message, { type: 'success' });
done(); done();
await onSearch(); await onSearch();
}, },
@ -215,7 +231,7 @@ export const onResetPassword = (row: any) => {
export const onAssignRolesToUser = (row: any) => { export const onAssignRolesToUser = (row: any) => {
addDialog({ addDialog({
title: `${row.username} 分配角色`, title: `${row.username} 分配角色`,
width: '30%', width: '45%',
draggable: true, draggable: true,
closeOnClickModal: false, closeOnClickModal: false,
fullscreenIcon: true, fullscreenIcon: true,