feat: 新增账户资料页与头像上传

将工作人员和用户的详情、编辑改为独立账户资料页。

增加受控头像上传与读取、图片安全校验、接口测试,并优化只读及编辑布局。

同步更新平台需求、接口安全说明、项目文档和操作日志。
This commit is contained in:
czl231
2026-08-10 22:35:47 +08:00
parent cef7223841
commit 7242048abf
20 changed files with 1351 additions and 13 deletions

View File

@@ -0,0 +1,62 @@
/**
* 功能:平台总后台头像上传与鉴权读取客户端。
* 版本v1.0.0
*/
import { getToken } from '@/utils/auth';
const platformApiBaseURL =
import.meta.env.VITE_API_BASE_URL ||
'http://localhost:12426/heqi/platform/v1';
export type AvatarUploadReply = {
uri: string;
original_name: string;
content_type: string;
size: number;
};
type ApiEnvelope<T> = { code?: number; message?: string; details?: T };
/** 生成服务根路径 URL确保上传请求不会错误拼接平台 API 前缀。 */
function serviceURL(path: string) {
const platformURL = new URL(platformApiBaseURL, window.location.origin);
return new URL(path, platformURL.origin).toString();
}
/** 返回与现有平台请求一致的 JWT 请求头。 */
function authorizationHeaders(): Record<string, string> {
const token = getToken();
return token ? { Authorization: token } : {};
}
/** 上传经过前端预检的头像文件,服务端仍会执行真实内容校验。 */
async function upload(file: File): Promise<AvatarUploadReply> {
const form = new FormData();
form.append('file', file);
const response = await fetch(serviceURL('/upload/avatar'), {
method: 'POST',
headers: authorizationHeaders(),
body: form,
});
const payload = (await response.json()) as ApiEnvelope<AvatarUploadReply>;
if (!response.ok || payload.code !== 0 || !payload.details) {
throw new Error(payload.message || '头像上传失败');
}
return payload.details;
}
/** 读取受保护头像;记录没有头像时返回 undefined 以使用本地默认图。 */
async function load(
resource: string,
identity: string,
): Promise<Blob | undefined> {
const response = await fetch(
`${platformApiBaseURL}${resource}/${encodeURIComponent(identity)}/avatar`,
{ headers: authorizationHeaders() },
);
if (response.status === 404) return undefined;
if (!response.ok) throw new Error('头像读取失败');
return response.blob();
}
export const avatarApi = { upload, load };