diff --git a/src/components/StudentManager.tsx b/src/components/StudentManager.tsx index 5060cf1..99fe008 100644 --- a/src/components/StudentManager.tsx +++ b/src/components/StudentManager.tsx @@ -14,7 +14,7 @@ import { Select, } from "antd" 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 { TagEditorDialog } from "./TagEditorDialog" 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 () => { if (!(window as any).api || !avatarStudent) return @@ -2197,6 +2219,13 @@ export const StudentManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { > {t("students.avatarUpload")} + diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 7e4b6ee..3a361a2 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -568,7 +568,11 @@ "avatarSaveFailed": "Failed to save avatar", "avatarInvalidFile": "Please choose an image file", "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": { "title": "Points Management", diff --git a/src/i18n/locales/ja-JP.json b/src/i18n/locales/ja-JP.json index 803974d..92b7750 100644 --- a/src/i18n/locales/ja-JP.json +++ b/src/i18n/locales/ja-JP.json @@ -575,7 +575,11 @@ "avatarSaveFailed": "アバターの保存に失敗しました", "avatarInvalidFile": "画像ファイルを選択してください", "avatarTooLarge": "画像は2MB以下にしてください", - "avatarReadFailed": "画像の読み取りに失敗しました" + "avatarReadFailed": "画像の読み取りに失敗しました", + "avatarClipboardImport": "クリップボードから取り込み", + "avatarClipboardUnsupported": "この環境ではクリップボード画像を読み取れません", + "avatarClipboardNoImage": "クリップボードに画像がありません", + "avatarClipboardReadFailed": "クリップボード画像の読み取りに失敗しました" }, "score": { "title": "ポイント管理", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 85b9938..52dc263 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -568,7 +568,11 @@ "avatarSaveFailed": "头像保存失败", "avatarInvalidFile": "请选择图片文件", "avatarTooLarge": "图片不能超过 2MB", - "avatarReadFailed": "读取图片失败" + "avatarReadFailed": "读取图片失败", + "avatarClipboardImport": "从剪贴板导入", + "avatarClipboardUnsupported": "当前环境不支持读取剪贴板图片", + "avatarClipboardNoImage": "剪贴板中没有图片", + "avatarClipboardReadFailed": "读取剪贴板图片失败" }, "score": { "title": "积分管理",