2026-08-11 00:49:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 功能:管理资源页头像的鉴权读取、本地预览、清除和保存前上传。
|
|
|
|
|
|
* 版本:v1.0.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { Message } from '@arco-design/web-vue';
|
|
|
|
|
|
import { onBeforeUnmount, ref } from 'vue';
|
|
|
|
|
|
import { avatarApi } from '@/api/avatar';
|
|
|
|
|
|
import { DEFAULT_USER_AVATAR } from '@/constants/avatar';
|
2026-08-11 13:37:44 +08:00
|
|
|
|
import { createAvatarUploadCache } from './avatar-upload-cache';
|
2026-08-11 00:49:41 +08:00
|
|
|
|
|
|
|
|
|
|
export function useResourceAvatar() {
|
|
|
|
|
|
const url = ref(DEFAULT_USER_AVATAR);
|
|
|
|
|
|
const file = ref<File>();
|
|
|
|
|
|
const cleared = ref(false);
|
|
|
|
|
|
const canClear = ref(false);
|
2026-08-11 13:37:44 +08:00
|
|
|
|
const uploadCache = createAvatarUploadCache(avatarApi.upload);
|
2026-08-11 00:49:41 +08:00
|
|
|
|
let objectURL = '';
|
|
|
|
|
|
|
|
|
|
|
|
function revoke() {
|
|
|
|
|
|
if (objectURL) URL.revokeObjectURL(objectURL);
|
|
|
|
|
|
objectURL = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 加载现有受保护头像,记录没有头像时继续使用默认图。 */
|
|
|
|
|
|
async function load(resource: string, identity: string) {
|
|
|
|
|
|
revoke();
|
2026-08-11 13:37:44 +08:00
|
|
|
|
uploadCache.reset();
|
2026-08-11 00:49:41 +08:00
|
|
|
|
url.value = DEFAULT_USER_AVATAR;
|
|
|
|
|
|
canClear.value = false;
|
|
|
|
|
|
file.value = undefined;
|
|
|
|
|
|
cleared.value = false;
|
|
|
|
|
|
if (!identity) return;
|
|
|
|
|
|
const blob = await avatarApi.load(resource, identity);
|
|
|
|
|
|
if (!blob) return;
|
|
|
|
|
|
objectURL = URL.createObjectURL(blob);
|
|
|
|
|
|
url.value = objectURL;
|
|
|
|
|
|
canClear.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 校验并预览用户选择的新头像。 */
|
|
|
|
|
|
function select(next: File) {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!['image/jpeg', 'image/png'].includes(next.type) ||
|
|
|
|
|
|
next.size <= 0 ||
|
|
|
|
|
|
next.size > 2 * 1024 * 1024
|
|
|
|
|
|
) {
|
|
|
|
|
|
Message.warning('请选择不超过 2 MB 的 JPG 或 PNG 图片');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
revoke();
|
2026-08-11 13:37:44 +08:00
|
|
|
|
uploadCache.reset();
|
2026-08-11 00:49:41 +08:00
|
|
|
|
objectURL = URL.createObjectURL(next);
|
|
|
|
|
|
url.value = objectURL;
|
|
|
|
|
|
file.value = next;
|
|
|
|
|
|
cleared.value = false;
|
|
|
|
|
|
canClear.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 标记清除头像,真正写入空值发生在保存资料时。 */
|
|
|
|
|
|
function clear() {
|
|
|
|
|
|
revoke();
|
2026-08-11 13:37:44 +08:00
|
|
|
|
uploadCache.reset();
|
2026-08-11 00:49:41 +08:00
|
|
|
|
url.value = DEFAULT_USER_AVATAR;
|
|
|
|
|
|
file.value = undefined;
|
|
|
|
|
|
cleared.value = true;
|
|
|
|
|
|
canClear.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 将头像变化写入资源更新载荷。 */
|
|
|
|
|
|
async function applyToPayload(payload: Record<string, unknown>) {
|
2026-08-11 13:37:44 +08:00
|
|
|
|
if (file.value) payload.avatar = await uploadCache.resolve(file.value);
|
2026-08-11 00:49:41 +08:00
|
|
|
|
else if (cleared.value) payload.avatar = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function marker() {
|
|
|
|
|
|
return file.value
|
|
|
|
|
|
? `${file.value.name}:${file.value.size}:${file.value.lastModified}`
|
|
|
|
|
|
: cleared.value
|
|
|
|
|
|
? 'clear'
|
|
|
|
|
|
: 'unchanged';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(revoke);
|
|
|
|
|
|
return {
|
|
|
|
|
|
url,
|
|
|
|
|
|
file,
|
|
|
|
|
|
cleared,
|
|
|
|
|
|
canClear,
|
|
|
|
|
|
load,
|
|
|
|
|
|
select,
|
|
|
|
|
|
clear,
|
|
|
|
|
|
applyToPayload,
|
|
|
|
|
|
marker,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|