2026-04-18-19-08-43 - 六项UI优化:基础字段无下划线、field-value联动高亮、视频按钮整合、视频间距紧凑、签名空行、图片占位符自适应高度

This commit is contained in:
Administrator
2026-04-18 19:14:23 +08:00
parent 5f4ae1ff29
commit 4a7051b6db
6 changed files with 242 additions and 23 deletions

View File

@@ -54,6 +54,7 @@ export default function ReportEditor() {
const prevVideoCountRef = useRef(0);
const [activeTab, setActiveTab] = useState<'info' | 'video'>('info');
const [activeFieldKey, setActiveFieldKey] = useState<string | null>(null);
const [multiSelectOptions, setMultiSelectOptions] = useState<Record<string, string[]>>({
surgeon: ['张医生', '李医生', '王医生'],
assistant: ['赵医生', '钱医生', '孙医生'],
@@ -378,13 +379,14 @@ export default function ReportEditor() {
const targetEl = node as HTMLElement | null;
if (!targetEl) return;
// Handle click on field-value: switch to info tab and focus corresponding input
// Handle click on field-value: switch to info tab, highlight and focus corresponding input
const fieldValue = targetEl.closest('.field-value') as HTMLElement | null;
if (fieldValue) {
const bindKey = fieldValue.getAttribute('data-bind');
if (bindKey) {
setActiveTab('info');
stateRef.current = { ...stateRef.current, activeTab: 'info' };
setActiveFieldKey(bindKey);
setTimeout(() => {
const inputEl = document.getElementById(`input-${bindKey}`);
if (inputEl) {
@@ -489,11 +491,14 @@ export default function ReportEditor() {
const fillPlaceholderSrc = (placeholder: HTMLElement, src: string) => {
placeholder.innerHTML = `
<span class="delete-btn" contenteditable="false">×</span>
<img src="${src}" style="max-width:100%;max-height:100%;object-fit:contain;display:block;margin:0 auto;" draggable="false">
<img src="${src}" style="max-width:100%;height:auto;display:block;margin:0 auto;" draggable="false">
`;
placeholder.classList.add('has-image');
placeholder.style.border = 'none';
placeholder.style.background = 'transparent';
placeholder.style.height = 'auto';
placeholder.style.width = 'auto';
placeholder.style.lineHeight = 'normal';
if (editorRef.current) contentRef.current = editorRef.current.innerHTML;
saveDraftToStorage();
};
@@ -664,6 +669,9 @@ export default function ReportEditor() {
emptyPlaceholder.classList.add('has-image');
emptyPlaceholder.style.border = 'none';
emptyPlaceholder.style.background = 'transparent';
emptyPlaceholder.style.height = 'auto';
emptyPlaceholder.style.width = 'auto';
emptyPlaceholder.style.lineHeight = 'normal';
contentRef.current = editorRef.current.innerHTML;
saveDraftToStorage();
}
@@ -690,11 +698,14 @@ export default function ReportEditor() {
const fillPlaceholder = (placeholder: HTMLElement, frame: CapturedFrame) => {
placeholder.innerHTML = `
<span class="delete-btn" contenteditable="false">×</span>
<img src="${frame.dataUrl}" style="max-width:100%;max-height:100%;object-fit:contain;display:block;margin:0 auto;" draggable="false">
<img src="${frame.dataUrl}" style="max-width:100%;height:auto;display:block;margin:0 auto;" draggable="false">
`;
placeholder.classList.add('has-image');
placeholder.style.border = 'none';
placeholder.style.background = 'transparent';
placeholder.style.height = 'auto';
placeholder.style.width = 'auto';
placeholder.style.lineHeight = 'normal';
if (editorRef.current) contentRef.current = editorRef.current.innerHTML;
saveDraftToStorage();
};
@@ -1453,7 +1464,7 @@ export default function ReportEditor() {
if (field.type === 'text' || field.type === 'date') {
const inputType = field.type === 'date' ? 'date' : 'text';
return (
<div key={field.key} id={`input-${field.key}`} className={field.category === '填空' && formFields.filter(f2 => f2.visibleInForm && f2.type === 'text' && f2.isSystemLocked).length > 1 && (field.key === 'patientName' || field.key === 'hospitalId') ? 'flex-1 space-y-1' : 'space-y-1'}>
<div key={field.key} id={`input-${field.key}`} className={`${field.category === '填空' && formFields.filter(f2 => f2.visibleInForm && f2.type === 'text' && f2.isSystemLocked).length > 1 && (field.key === 'patientName' || field.key === 'hospitalId') ? 'flex-1 space-y-1' : 'space-y-1'} p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
<label className="block text-xs font-bold text-text-main">
{field.label} {isRequired && <span className="text-red-500">*</span>}
</label>
@@ -1473,7 +1484,7 @@ export default function ReportEditor() {
const isOpen = openDropdown === field.key;
const opts = field.options || (field.key === 'anesthesiaType' ? anesthesiaOptions : []);
return (
<div key={field.key} id={`input-${field.key}`} className="space-y-1 select-dropdown-root relative">
<div key={field.key} id={`input-${field.key}`} className={`space-y-1 select-dropdown-root relative p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
<label className="block text-xs font-bold text-text-main">{field.label}</label>
<div
className="w-full px-3 py-2 border border-border rounded-lg bg-white flex items-center min-h-[42px] cursor-text"
@@ -1582,7 +1593,7 @@ export default function ReportEditor() {
const currentInputText = multiInputText[field.key] !== undefined ? multiInputText[field.key] : displayText;
return (
<div key={field.key} id={`input-${field.key}`} className="space-y-1 select-dropdown-root relative">
<div key={field.key} id={`input-${field.key}`} className={`space-y-1 select-dropdown-root relative p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
<label className="block text-xs font-bold text-text-main">{field.label}</label>
<div
className="w-full px-3 py-2 border border-border rounded-lg bg-white flex flex-wrap gap-1 items-center min-h-[42px] cursor-text"
@@ -1663,7 +1674,7 @@ export default function ReportEditor() {
const { h: h12, isPM } = from24h(h24val);
return (
<div key={field.key} id={`input-${field.key}`} className="space-y-1">
<div key={field.key} id={`input-${field.key}`} className={`space-y-1 p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
<label className="block text-xs font-bold text-text-main">{field.label}</label>
<div className="flex items-center gap-2">
<select
@@ -1777,7 +1788,7 @@ export default function ReportEditor() {
)}
{activeTab === 'video' && (
<div className="space-y-4">
<div className="space-y-2">
<input
ref={videoInputRef}
type="file"
@@ -1786,20 +1797,17 @@ export default function ReportEditor() {
className="hidden"
onChange={handleVideoUpload}
/>
<button
onClick={() => videoInputRef.current?.click()}
className="w-full flex items-center justify-center gap-2 p-3 border border-dashed border-border rounded-lg hover:border-accent hover:bg-slate-50 transition-all"
>
<Video size={18} />
<div className="text-left">
<p className="text-xs font-bold text-text-main"></p>
<p className="text-[10px] text-text-muted"> MP4, MOV </p>
</div>
</button>
{videos.length > 0 && (
<div className="space-y-4">
<div className="space-y-2">
<div className="flex gap-2 overflow-x-auto pb-2 no-scrollbar">
<button
onClick={() => videoInputRef.current?.click()}
className="shrink-0 w-24 h-[68px] flex flex-col items-center justify-center gap-1 border-2 border-dashed border-border rounded-xl hover:border-accent hover:bg-slate-50 transition-all text-text-muted hover:text-accent"
>
<Video size={18} />
<span className="text-[10px] font-bold"></span>
</button>
{videos.map((v, i) => (
<div
key={v.id}
@@ -1828,7 +1836,7 @@ export default function ReportEditor() {
</div>
{currentVideoIndex !== -1 && (
<div className="space-y-4">
<div className="space-y-2">
<div className="relative bg-slate-900 rounded-2xl overflow-hidden aspect-video shadow-lg">
<video
ref={videoRef}
@@ -1866,7 +1874,7 @@ export default function ReportEditor() {
</div>
</div>
<div className="flex justify-between items-center pt-2">
<div className="flex justify-between items-center pt-1 border-t border-border">
<span className="text-[10px] font-bold text-text-main uppercase tracking-wider"></span>
<button
onClick={captureFrame}