From f373520fa594bd6e34de25f9c7b6525b291f3ed9 Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Thu, 16 Apr 2026 22:29:40 +0800 Subject: [PATCH] =?UTF-8?q?2026-04-16-22-23-02=20-=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?TemplateManage=20=E5=AD=97=E6=AE=B5=E5=BA=93=E4=B8=8E=20ReportE?= =?UTF-8?q?ditor=20=E5=8F=8C=E5=90=91=E6=95=B0=E6=8D=AE=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E6=99=BA=E8=83=BD=E5=8D=A0=E4=BD=8D=E6=96=B9=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.css | 32 +++ src/pages/ReportEditor.tsx | 44 +++- src/pages/TemplateManage.tsx | 139 ++++++---- src/types.ts | 22 ++ src/utils/print.ts | 6 + 工程分析/实现方案-2026-04-16-22-23-02.md | 249 ++++++++++++++++++ 工程分析/测试方案-2026-04-16-22-23-02.md | 102 ++++++++ 工程分析/经验记录.md | 311 +++++++++++++++++++++++ 工程分析/需求分析-2026-04-16-22-23-02.md | 50 ++++ 9 files changed, 907 insertions(+), 48 deletions(-) create mode 100644 工程分析/实现方案-2026-04-16-22-23-02.md create mode 100644 工程分析/测试方案-2026-04-16-22-23-02.md create mode 100644 工程分析/经验记录.md create mode 100644 工程分析/需求分析-2026-04-16-22-23-02.md diff --git a/src/index.css b/src/index.css index a16fc6d..540702c 100644 --- a/src/index.css +++ b/src/index.css @@ -95,6 +95,31 @@ .manual-frame-badge { @apply absolute top-1 left-1 px-1.5 py-0.5 bg-yellow-400 text-yellow-900 text-[9px] font-bold rounded shadow-sm pointer-events-none; } + + /* Smart Field Bindable Controls */ + .smart-field-wrapper { + display: inline-flex; + align-items: center; + margin: 0 4px; + vertical-align: middle; + } + .smart-field-wrapper .field-label { + color: #64748b; + user-select: none; + } + .smart-field-wrapper .field-value { + min-width: 60px; + padding: 0 4px; + border: 1px solid #cbd5e1; + border-radius: 4px; + display: inline-block; + background: #fff; + color: #0f172a; + outline: none; + } + .smart-field-wrapper .field-value:empty::before { + content: '\200b'; + } } @media print { @@ -124,4 +149,11 @@ .print-content .image-placeholder:not(.has-image) { display: none !important; } + .print-content .smart-field-wrapper .field-value { + border: none !important; + border-bottom: 1px solid #000 !important; + border-radius: 0 !important; + background: transparent !important; + padding: 0 2px !important; + } } diff --git a/src/pages/ReportEditor.tsx b/src/pages/ReportEditor.tsx index 4012613..ec375ef 100644 --- a/src/pages/ReportEditor.tsx +++ b/src/pages/ReportEditor.tsx @@ -873,6 +873,48 @@ export default function ReportEditor() { if (status === 'completed') navigate('/report-manage'); }; + const handleEditorInput = (e: React.FormEvent) => { + if (editorRef.current) { + contentRef.current = editorRef.current.innerHTML; + } + updatePageHeight(); + saveDraftToStorage(); + + const target = e.target as HTMLElement; + if (target && target.hasAttribute('data-bind')) { + const fieldKey = target.getAttribute('data-bind')!; + const newValue = target.innerText; + + setReportData((prev) => { + const next = { ...prev, [fieldKey]: newValue }; + stateRef.current = { ...stateRef.current, reportData: next }; + return next; + }); + } + }; + + // Sync form state -> rich text field values + useEffect(() => { + if (!editorRef.current) return; + const bindNodes = editorRef.current.querySelectorAll('[data-bind]'); + bindNodes.forEach((node) => { + const el = node as HTMLElement; + const fieldKey = el.getAttribute('data-bind')!; + const rawValue = (reportData as any)[fieldKey]; + + let newValue = ''; + if (Array.isArray(rawValue)) { + newValue = rawValue.join(', '); + } else if (rawValue !== undefined && rawValue !== null) { + newValue = String(rawValue); + } + + if (el.innerText !== newValue) { + el.innerText = newValue; + } + }); + }, [reportData]); + if (!currentUser) return null; const hasVisibleTemplates = templates.length > 0; @@ -1006,7 +1048,7 @@ export default function ReportEditor() {
{ contentRef.current = editorRef.current?.innerHTML || ''; updatePageHeight(); saveDraftToStorage(); }} + onInput={handleEditorInput} onBlur={() => { contentRef.current = editorRef.current?.innerHTML || ''; saveDraftToStorage(); }} className="editor-content print-content" > diff --git a/src/pages/TemplateManage.tsx b/src/pages/TemplateManage.tsx index f36aa15..4299ad7 100644 --- a/src/pages/TemplateManage.tsx +++ b/src/pages/TemplateManage.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import Sidebar from '../components/Sidebar'; import { Plus, Edit, Trash2, Save, Printer, Undo, Redo, Bold, Italic, Underline, AlignLeft, AlignCenter, AlignRight, Table, Image as ImageIcon, Check } from 'lucide-react'; -import { User, Template } from '../types'; +import { User, Template, BINDABLE_FIELDS } from '../types'; import { defaultReportContent } from '../utils/defaultContent'; import { printDocument } from '../utils/print'; import { storage } from '../utils/storage'; @@ -156,6 +156,22 @@ export default function TemplateManage() { editorRef.current?.focus(); }; + const insertSmartField = (field: typeof BINDABLE_FIELDS[0]) => { + editorRef.current?.focus(); + const html = ` + + ${field.label}: + + +   + `; + document.execCommand('insertHTML', false, html); + editorRef.current?.focus(); + }; + const insertTable = () => { const rowsStr = prompt('请输入行数:', '2'); const colsStr = prompt('请输入列数:', '3'); @@ -396,58 +412,87 @@ export default function TemplateManage() {
-
- {/* Toolbar */} -
-
- - -
-
- -
-
- - - -
- execCmd('foreColor', e.target.value)} - className="w-9 h-9 p-1.5 bg-transparent border-none cursor-pointer rounded-lg hover:bg-white transition-colors" - title="文字颜色" - /> +
+ {/* Editor Main */} +
+ {/* Toolbar */} +
+
+ + +
+
+ +
+
+ + + +
+ execCmd('foreColor', e.target.value)} + className="w-9 h-9 p-1.5 bg-transparent border-none cursor-pointer rounded-lg hover:bg-white transition-colors" + title="文字颜色" + /> +
+
+
+ + + +
+
+