feat: 头像设置支持从剪贴板导入图片

This commit is contained in:
JSR
2026-04-18 11:29:27 +08:00
parent e82866bbfc
commit 0188bdcea0
4 changed files with 45 additions and 4 deletions
+30 -1
View File
@@ -14,7 +14,7 @@ import {
Select, Select,
} from "antd" } from "antd"
import type { ColumnsType } from "antd/es/table" import type { ColumnsType } from "antd/es/table"
import { UploadOutlined, MoreOutlined, PlusOutlined } from "@ant-design/icons" import { UploadOutlined, MoreOutlined, PlusOutlined, CopyOutlined } from "@ant-design/icons"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { TagEditorDialog } from "./TagEditorDialog" import { TagEditorDialog } from "./TagEditorDialog"
import { getAvatarFromExtraJson, setAvatarInExtraJson } from "../utils/studentAvatar" import { getAvatarFromExtraJson, setAvatarInExtraJson } from "../utils/studentAvatar"
@@ -720,6 +720,28 @@ export const StudentManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
} }
} }
const handleAvatarPasteFromClipboard = async () => {
if (typeof navigator === "undefined" || !navigator.clipboard?.read) {
messageApi.error(t("students.avatarClipboardUnsupported"))
return
}
try {
const clipboardItems = await navigator.clipboard.read()
for (const item of clipboardItems) {
const imageType = item.types.find((type) => type.startsWith("image/"))
if (!imageType) continue
const blob = await item.getType(imageType)
const ext = imageType.split("/")[1] || "png"
const file = new File([blob], `avatar-clipboard.${ext}`, { type: imageType })
await handleAvatarFileChange(file)
return
}
messageApi.warning(t("students.avatarClipboardNoImage"))
} catch {
messageApi.error(t("students.avatarClipboardReadFailed"))
}
}
const handleSaveAvatar = async () => { const handleSaveAvatar = async () => {
if (!(window as any).api || !avatarStudent) return if (!(window as any).api || !avatarStudent) return
@@ -2197,6 +2219,13 @@ export const StudentManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
> >
{t("students.avatarUpload")} {t("students.avatarUpload")}
</Button> </Button>
<Button
icon={<CopyOutlined />}
onClick={handleAvatarPasteFromClipboard}
disabled={!canEdit}
>
{t("students.avatarClipboardImport")}
</Button>
<Button onClick={() => setAvatarValue(null)} disabled={!canEdit}> <Button onClick={() => setAvatarValue(null)} disabled={!canEdit}>
{t("students.avatarClear")} {t("students.avatarClear")}
</Button> </Button>
+5 -1
View File
@@ -568,7 +568,11 @@
"avatarSaveFailed": "Failed to save avatar", "avatarSaveFailed": "Failed to save avatar",
"avatarInvalidFile": "Please choose an image file", "avatarInvalidFile": "Please choose an image file",
"avatarTooLarge": "Image must be smaller than 2MB", "avatarTooLarge": "Image must be smaller than 2MB",
"avatarReadFailed": "Failed to read image" "avatarReadFailed": "Failed to read image",
"avatarClipboardImport": "Import from Clipboard",
"avatarClipboardUnsupported": "Clipboard image read is not supported",
"avatarClipboardNoImage": "No image found in clipboard",
"avatarClipboardReadFailed": "Failed to read clipboard image"
}, },
"score": { "score": {
"title": "Points Management", "title": "Points Management",
+5 -1
View File
@@ -575,7 +575,11 @@
"avatarSaveFailed": "アバターの保存に失敗しました", "avatarSaveFailed": "アバターの保存に失敗しました",
"avatarInvalidFile": "画像ファイルを選択してください", "avatarInvalidFile": "画像ファイルを選択してください",
"avatarTooLarge": "画像は2MB以下にしてください", "avatarTooLarge": "画像は2MB以下にしてください",
"avatarReadFailed": "画像の読み取りに失敗しました" "avatarReadFailed": "画像の読み取りに失敗しました",
"avatarClipboardImport": "クリップボードから取り込み",
"avatarClipboardUnsupported": "この環境ではクリップボード画像を読み取れません",
"avatarClipboardNoImage": "クリップボードに画像がありません",
"avatarClipboardReadFailed": "クリップボード画像の読み取りに失敗しました"
}, },
"score": { "score": {
"title": "ポイント管理", "title": "ポイント管理",
+5 -1
View File
@@ -568,7 +568,11 @@
"avatarSaveFailed": "头像保存失败", "avatarSaveFailed": "头像保存失败",
"avatarInvalidFile": "请选择图片文件", "avatarInvalidFile": "请选择图片文件",
"avatarTooLarge": "图片不能超过 2MB", "avatarTooLarge": "图片不能超过 2MB",
"avatarReadFailed": "读取图片失败" "avatarReadFailed": "读取图片失败",
"avatarClipboardImport": "从剪贴板导入",
"avatarClipboardUnsupported": "当前环境不支持读取剪贴板图片",
"avatarClipboardNoImage": "剪贴板中没有图片",
"avatarClipboardReadFailed": "读取剪贴板图片失败"
}, },
"score": { "score": {
"title": "积分管理", "title": "积分管理",