fix: 讯飞配置默认值+AI图片同步+TemplateManage空行清理

- ReportEditor: 讯飞语音读取xfSpeechConfig增加DEFAULT_XF_SPEECH后备,旧用户无需手动保存
- ReportEditor: 切换到AI面板时强制同步编辑器图片到视觉参考上下文
- TemplateManage: saveCurrentTemplate/saveTemplateContent保存前清理空段落和标签间空白
- types.ts: 新增XfSpeechConfig接口和DEFAULT_XF_SPEECH常量
This commit is contained in:
2026-04-20 01:01:01 +08:00
parent 9f2b5dce21
commit ea789cee26
3 changed files with 43 additions and 5 deletions

View File

@@ -8,7 +8,7 @@ import {
Video, Play, Pause, Plus, X, ChevronLeft, Download, Video, Play, Pause, Plus, X, ChevronLeft, Download,
Bot, Mic, MicOff, ImagePlus, Sparkles, Send Bot, Mic, MicOff, ImagePlus, Sparkles, Send
} from 'lucide-react'; } from 'lucide-react';
import { User, Report, Template, CapturedFrame, SystemSettings, FormField, DEFAULT_FORM_FIELDS } from '../types'; import { User, Report, Template, CapturedFrame, SystemSettings, FormField, DEFAULT_FORM_FIELDS, DEFAULT_XF_SPEECH } from '../types';
import { defaultReportContent } from '../utils/defaultContent'; import { defaultReportContent } from '../utils/defaultContent';
import { printDocument } from '../utils/print'; import { printDocument } from '../utils/print';
import { storage, getDefaultApiKey } from '../utils/storage'; import { storage, getDefaultApiKey } from '../utils/storage';
@@ -115,6 +115,18 @@ export default function ReportEditor() {
stateRef.current.chatInput = chatInput; stateRef.current.chatInput = chatInput;
}, [chatInput]); }, [chatInput]);
// 切换到 AI 面板时强制同步编辑器中的图片
useEffect(() => {
if (activeTab !== 'ai' || !editorRef.current) return;
const imgs = Array.from(editorRef.current.querySelectorAll('.image-placeholder.has-image img'))
.map((img, idx) => ({ id: `editor-img-${idx}-${(img as HTMLImageElement).src.slice(-16)}`, src: (img as HTMLImageElement).src }))
.filter(img => img.src);
setEditorImages(prev => {
const same = prev.length === imgs.length && prev.every((p, i) => p.src === imgs[i]?.src);
return same ? prev : imgs;
});
}, [activeTab]);
useEffect(() => { useEffect(() => {
if (!editorRef.current) return; if (!editorRef.current) return;
const allFields = editorRef.current.querySelectorAll('.field-value'); const allFields = editorRef.current.querySelectorAll('.field-value');
@@ -920,7 +932,7 @@ export default function ReportEditor() {
if (xfMediaStreamRef.current) { xfMediaStreamRef.current.getTracks().forEach(t => t.stop()); xfMediaStreamRef.current = null; } if (xfMediaStreamRef.current) { xfMediaStreamRef.current.getTracks().forEach(t => t.stop()); xfMediaStreamRef.current = null; }
return; return;
} }
const xfConfig = storage.get<SystemSettings>('systemSettings', {} as SystemSettings).xfSpeechConfig; const xfConfig = storage.get<SystemSettings>('systemSettings', {} as SystemSettings).xfSpeechConfig || DEFAULT_XF_SPEECH;
if (!xfConfig?.appId || !xfConfig?.apiKey || !xfConfig?.apiSecret) { if (!xfConfig?.appId || !xfConfig?.apiKey || !xfConfig?.apiSecret) {
alert('请先在系统设置中配置讯飞语音 APPID/APIKey/APISecret'); alert('请先在系统设置中配置讯飞语音 APPID/APIKey/APISecret');
return; return;

View File

@@ -419,9 +419,16 @@ export default function TemplateManage() {
const saveTemplateContent = () => { const saveTemplateContent = () => {
if (!currentTemplateId || !editorRef.current) return; if (!currentTemplateId || !editorRef.current) return;
let cleanContent = editorRef.current.innerHTML;
cleanContent = cleanContent.replace(/<p>\s*<br\s*\/?>\s*<\/p>/gi, '');
cleanContent = cleanContent.replace(/<p><\/p>/gi, '');
cleanContent = cleanContent.replace(/>(\s+)</g, '><');
if (cleanContent !== editorRef.current.innerHTML) {
editorRef.current.innerHTML = cleanContent;
}
const allTemplates = storage.get<Template[]>('templates', []); const allTemplates = storage.get<Template[]>('templates', []);
const updated = allTemplates.map(t => const updated = allTemplates.map(t =>
t.id === currentTemplateId ? { ...t, content: editorRef.current!.innerHTML, updatedAt: new Date().toISOString() } : t t.id === currentTemplateId ? { ...t, content: cleanContent, updatedAt: new Date().toISOString() } : t
); );
setTemplates(prevTemplates => prevTemplates.map(t => updated.find(u => u.id === t.id) || t)); setTemplates(prevTemplates => prevTemplates.map(t => updated.find(u => u.id === t.id) || t));
storage.set('templates', updated); storage.set('templates', updated);
@@ -603,10 +610,17 @@ export default function TemplateManage() {
const saveCurrentTemplate = () => { const saveCurrentTemplate = () => {
if (!currentTemplateId || !editorRef.current) return; if (!currentTemplateId || !editorRef.current) return;
let cleanContent = editorRef.current.innerHTML;
cleanContent = cleanContent.replace(/<p>\s*<br\s*\/?>\s*<\/p>/gi, '');
cleanContent = cleanContent.replace(/<p><\/p>/gi, '');
cleanContent = cleanContent.replace(/>(\s+)</g, '><');
if (cleanContent !== editorRef.current.innerHTML) {
editorRef.current.innerHTML = cleanContent;
}
const allTemplates = storage.get<Template[]>('templates', []); const allTemplates = storage.get<Template[]>('templates', []);
const updated = allTemplates.map(t => { const updated = allTemplates.map(t => {
if (t.id === currentTemplateId) { if (t.id === currentTemplateId) {
return { ...t, content: editorRef.current!.innerHTML, updatedAt: new Date().toISOString() }; return { ...t, content: cleanContent, updatedAt: new Date().toISOString() };
} }
return t; return t;
}); });

View File

@@ -76,6 +76,18 @@ export interface AiProviderConfig {
modelName: string; modelName: string;
} }
export interface XfSpeechConfig {
appId: string;
apiKey: string;
apiSecret: string;
}
export const DEFAULT_XF_SPEECH: XfSpeechConfig = {
appId: 'e0fe23e3',
apiKey: '7fd08be316718c2280e85af4fe126306',
apiSecret: 'ZGI5MjAzZDA0YzYwNDhjMWZiNTM2NDE0'
};
export interface SystemSettings { export interface SystemSettings {
frameCount: number; frameCount: number;
framePositions: number[]; framePositions: number[];
@@ -86,7 +98,7 @@ export interface SystemSettings {
autoInsertDelay?: number; autoInsertDelay?: number;
activeAiProvider: string; activeAiProvider: string;
aiProviders: Record<string, AiProviderConfig>; aiProviders: Record<string, AiProviderConfig>;
xfSpeechConfig?: { appId: string; apiKey: string; apiSecret: string }; xfSpeechConfig?: XfSpeechConfig;
} }
export const DEFAULT_AI_PROVIDERS: Record<string, AiProviderConfig> = { export const DEFAULT_AI_PROVIDERS: Record<string, AiProviderConfig> = {