mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 21:14:21 +08:00
feat: 头像设置支持从剪贴板导入图片
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -575,7 +575,11 @@
|
|||||||
"avatarSaveFailed": "アバターの保存に失敗しました",
|
"avatarSaveFailed": "アバターの保存に失敗しました",
|
||||||
"avatarInvalidFile": "画像ファイルを選択してください",
|
"avatarInvalidFile": "画像ファイルを選択してください",
|
||||||
"avatarTooLarge": "画像は2MB以下にしてください",
|
"avatarTooLarge": "画像は2MB以下にしてください",
|
||||||
"avatarReadFailed": "画像の読み取りに失敗しました"
|
"avatarReadFailed": "画像の読み取りに失敗しました",
|
||||||
|
"avatarClipboardImport": "クリップボードから取り込み",
|
||||||
|
"avatarClipboardUnsupported": "この環境ではクリップボード画像を読み取れません",
|
||||||
|
"avatarClipboardNoImage": "クリップボードに画像がありません",
|
||||||
|
"avatarClipboardReadFailed": "クリップボード画像の読み取りに失敗しました"
|
||||||
},
|
},
|
||||||
"score": {
|
"score": {
|
||||||
"title": "ポイント管理",
|
"title": "ポイント管理",
|
||||||
|
|||||||
@@ -568,7 +568,11 @@
|
|||||||
"avatarSaveFailed": "头像保存失败",
|
"avatarSaveFailed": "头像保存失败",
|
||||||
"avatarInvalidFile": "请选择图片文件",
|
"avatarInvalidFile": "请选择图片文件",
|
||||||
"avatarTooLarge": "图片不能超过 2MB",
|
"avatarTooLarge": "图片不能超过 2MB",
|
||||||
"avatarReadFailed": "读取图片失败"
|
"avatarReadFailed": "读取图片失败",
|
||||||
|
"avatarClipboardImport": "从剪贴板导入",
|
||||||
|
"avatarClipboardUnsupported": "当前环境不支持读取剪贴板图片",
|
||||||
|
"avatarClipboardNoImage": "剪贴板中没有图片",
|
||||||
|
"avatarClipboardReadFailed": "读取剪贴板图片失败"
|
||||||
},
|
},
|
||||||
"score": {
|
"score": {
|
||||||
"title": "积分管理",
|
"title": "积分管理",
|
||||||
|
|||||||
Reference in New Issue
Block a user