From 18db27829efe99061b8f54345146b9af638217fd Mon Sep 17 00:00:00 2001 From: zihanjian Date: Sun, 4 May 2025 19:54:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=B0=83=E8=89=B2=E6=9D=BF=E5=AF=BC=E5=85=A5=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BC=98=E5=8C=96=E8=B0=83=E8=89=B2?= =?UTF-8?q?=E6=9D=BF=E7=BC=96=E8=BE=91=E5=99=A8=E4=BA=A4=E4=BA=92=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E3=80=82=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E4=BB=A5=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E6=8F=90=E4=BE=9B=E5=AF=BC=E5=87=BA=E9=80=89?= =?UTF-8?q?=E4=B8=AD=E9=A2=9C=E8=89=B2=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E5=9C=A8=E7=95=8C=E9=9D=A2=E4=B8=AD=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=9B=B8=E5=BA=94=E6=8C=89=E9=92=AE=E3=80=82=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91=E4=BB=A5=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=93=8D=E4=BD=9C=E7=9A=84=E6=B5=81=E7=95=85?= =?UTF-8?q?=E6=80=A7=E5=92=8C=E6=9C=89=E6=95=88=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/page.tsx | 109 +++++++++++++++++++++++-- src/components/CustomPaletteEditor.tsx | 28 ++++++- 2 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 44b2bfb..8fc748f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -157,7 +157,9 @@ export default function Home() { const originalCanvasRef = useRef(null); const pixelatedCanvasRef = useRef(null); const fileInputRef = useRef(null); - // const longPressTimerRef = useRef(null); + // ++ 添加: Ref for import file input ++ + const importPaletteInputRef = useRef(null); + //const longPressTimerRef = useRef(null); // ++ Re-add touch refs needed for tooltip logic ++ //const touchStartPosRef = useRef<{ x: number; y: number; pageX: number; pageY: number } | null>(null); //const touchMovedRef = useRef(false); @@ -1057,9 +1059,10 @@ export default function Home() { paletteOptions[typedPresetKey].keys || [] ); setCustomPaletteSelections(newSelections); - setSelectedPaletteKeySet(typedPresetKey); - setIsCustomPalette(false); - setIsCustomPaletteEditorOpen(false); + setSelectedPaletteKeySet(typedPresetKey); // 同步更新预设选择状态 + setIsCustomPalette(false); // 应用预设后,标记为非自定义(除非用户再次修改) + // 不要在这里关闭编辑器,让用户可以继续编辑 + // setIsCustomPaletteEditorOpen(false); }; // 保存自定义色板并应用 @@ -1074,6 +1077,89 @@ export default function Home() { setSelectedColor(null); }; + // ++ 新增:导出自定义色板配置 ++ + const handleExportCustomPalette = () => { + const selectedKeys = Object.entries(customPaletteSelections) + .filter(([, isSelected]) => isSelected) + .map(([key]) => key); + + if (selectedKeys.length === 0) { + alert("当前没有选中的颜色,无法导出。"); + return; + } + + const blob = new Blob([JSON.stringify({ selectedKeys }, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = 'custom-perler-palette.json'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + // ++ 新增:处理导入的色板文件 ++ + const handleImportPaletteFile = (event: ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const content = e.target?.result as string; + const data = JSON.parse(content); + + if (!data || !Array.isArray(data.selectedKeys)) { + throw new Error("无效的文件格式:缺少 'selectedKeys' 数组。"); + } + + const importedKeys = data.selectedKeys as string[]; + const validKeys = new Set(allPaletteKeys); + const validImportedKeys = importedKeys.filter(key => validKeys.has(key)); + const invalidKeys = importedKeys.filter(key => !validKeys.has(key)); + + if (invalidKeys.length > 0) { + console.warn("导入时发现无效的颜色key:", invalidKeys); + alert(`导入完成,但以下色号无效已被忽略:\n${invalidKeys.join(', ')}`); + } + + if (validImportedKeys.length === 0) { + alert("导入的文件中不包含任何有效的色号。"); + return; + } + + // 基于有效导入的key创建新的selections对象 + const newSelections = presetToSelections(allPaletteKeys, validImportedKeys); + setCustomPaletteSelections(newSelections); + setIsCustomPalette(true); // 标记为自定义 + alert(`成功导入 ${validImportedKeys.length} 个色号!`); + + } catch (error) { + console.error("导入色板配置失败:", error); + alert(`导入失败: ${error instanceof Error ? error.message : '未知错误'}`); + } finally { + // 重置文件输入,以便可以再次导入相同的文件 + if (event.target) { + event.target.value = ''; + } + } + }; + reader.onerror = () => { + alert("读取文件失败。"); + // 重置文件输入 + if (event.target) { + event.target.value = ''; + } + }; + reader.readAsText(file); + }; + + // ++ 新增:触发导入文件选择 ++ + const triggerImportPalette = () => { + importPaletteInputRef.current?.click(); + }; + return ( <> {/* 添加自定义动画样式 */} @@ -1321,8 +1407,16 @@ export default function Home() { {/* 自定义色板编辑器弹窗 - 这是新增的部分 */} {isCustomPaletteEditorOpen && (
-
-
+
+ {/* 添加隐藏的文件输入框 */} + +
{/* 让内容区域可滚动 */} setIsCustomPaletteEditorOpen(false)} paletteOptions={paletteOptions} + // ++ 传递新的处理函数 ++ + onExportCustomPalette={handleExportCustomPalette} + onImportCustomPalette={triggerImportPalette} />
diff --git a/src/components/CustomPaletteEditor.tsx b/src/components/CustomPaletteEditor.tsx index 1c11eb0..cfbd1cc 100644 --- a/src/components/CustomPaletteEditor.tsx +++ b/src/components/CustomPaletteEditor.tsx @@ -36,6 +36,8 @@ interface CustomPaletteEditorProps { onSaveCustomPalette: () => void; onClose: () => void; paletteOptions: Record; + onExportCustomPalette: () => void; + onImportCustomPalette: () => void; } const CustomPaletteEditor: React.FC = ({ @@ -45,7 +47,9 @@ const CustomPaletteEditor: React.FC = ({ onApplyPreset, onSaveCustomPalette, onClose, - paletteOptions + paletteOptions, + onExportCustomPalette, + onImportCustomPalette, }) => { // 用于跟踪当前展开的颜色组 const [expandedGroups, setExpandedGroups] = useState>({}); @@ -91,7 +95,7 @@ const CustomPaletteEditor: React.FC = ({ }; return ( -
+
{/* 头部 */}

@@ -158,7 +162,7 @@ const CustomPaletteEditor: React.FC = ({

{/* 快捷操作按钮 */} -
+
+ +
{/* 颜色列表 */}