init: initial commit
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import { Shield, UploadCloud, FileCheck, Loader2, RefreshCw } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useAppStore, useCurrentProject } from '../../stores/useAppStore';
|
||||
import { useUIStore } from '../../stores/useUIStore';
|
||||
import { useChatStore } from '../../stores/useChatStore';
|
||||
|
||||
import { ParseDeliveryStandard } from '../../../bindings/engimind/internal/parser/parseservice.js';
|
||||
import { StreamTemplateDirectory } from '../../../bindings/engimind/internal/chat/chatservice.js';
|
||||
import { Events } from '@wailsio/runtime';
|
||||
|
||||
export function TemplateCard() {
|
||||
const currentProject = useCurrentProject();
|
||||
const { isParsingTemplate, hasNewTemplatePending, setParsingTemplate, setNewTemplatePending, setEditingOutline } = useUIStore();
|
||||
const setProjects = useAppStore(s => s.setProjects);
|
||||
const currentProjectId = useAppStore(s => s.currentProjectId);
|
||||
const [pendingMarkdown, setPendingMarkdown] = useState('');
|
||||
|
||||
const startTemplateParse = async () => {
|
||||
if (!pendingMarkdown) return;
|
||||
|
||||
try {
|
||||
setParsingTemplate(true);
|
||||
setNewTemplatePending(false);
|
||||
|
||||
const activeModelId = useAppStore.getState().activeModelId;
|
||||
if (!activeModelId) {
|
||||
alert("提示:请先在配置中心连接一个有效的大语言模型提供商,否则无法进行文件解析!");
|
||||
setParsingTemplate(false);
|
||||
setNewTemplatePending(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const cs = useChatStore.getState();
|
||||
const userMsgId = Date.now();
|
||||
const assistantMsgId = userMsgId + 1;
|
||||
const msgIdStr = assistantMsgId.toString();
|
||||
|
||||
cs.addMessage({ id: userMsgId, role: 'user', content: '我上传了一份工程交付文档。请帮我深度解析并归纳出标准大纲结构。' });
|
||||
cs.addMessage({ id: assistantMsgId, role: 'assistant', content: '', status: 'processing' });
|
||||
|
||||
let unSub = Events.On("chat_stream_" + msgIdStr, (e: any) => {
|
||||
const fullText = Array.isArray(e.data) ? e.data[0] : e.data;
|
||||
if (typeof fullText === 'string') {
|
||||
const msg = cs.messages.find(m => m.id === assistantMsgId);
|
||||
if (msg && fullText.length > msg.content.length) {
|
||||
cs.updateMessage(assistantMsgId, { content: fullText });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Invoke LLM to extract standard chapters
|
||||
let jsonStr = await StreamTemplateDirectory(pendingMarkdown, activeModelId, msgIdStr);
|
||||
unSub();
|
||||
|
||||
cs.updateMessage(assistantMsgId, { status: 'success' });
|
||||
|
||||
let jsonStrClean = jsonStr.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
||||
jsonStrClean = jsonStrClean.replace(/```json/g, '').replace(/```/g, '').trim();
|
||||
|
||||
const startIdx = jsonStrClean.indexOf('[');
|
||||
const endIdx = jsonStrClean.lastIndexOf(']');
|
||||
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
||||
jsonStrClean = jsonStrClean.substring(startIdx, endIdx + 1);
|
||||
}
|
||||
|
||||
let parsedChapters;
|
||||
try {
|
||||
parsedChapters = JSON.parse(jsonStrClean);
|
||||
} catch (err) {
|
||||
console.error("LLM json decode failed:", jsonStrClean);
|
||||
alert("模型解析出的结果不符合要求格式,请重试!\n模型原始输出片段:" + jsonStrClean.substring(0, 100) + "...");
|
||||
setParsingTemplate(false);
|
||||
setNewTemplatePending(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(parsedChapters)) {
|
||||
parsedChapters = parsedChapters.chapters || parsedChapters.data || [];
|
||||
if (!Array.isArray(parsedChapters)) {
|
||||
parsedChapters = [parsedChapters];
|
||||
}
|
||||
}
|
||||
|
||||
const chapters = parsedChapters.map((c: any, i: number) => ({
|
||||
id: c.id || `generated-${Date.now()}-${i}`,
|
||||
title: c.title || `第${i+1}节 内容生成`,
|
||||
status: 'idle',
|
||||
progress: 0,
|
||||
content: c.content || ''
|
||||
}));
|
||||
|
||||
// Update project directory globally
|
||||
setProjects(prev => prev.map(p => {
|
||||
if (p.id !== currentProjectId) return p;
|
||||
return {
|
||||
...p,
|
||||
activeTemplate: {
|
||||
name: 'AI 深层解析大纲', version: 'v1.0 (Auto)',
|
||||
chapters,
|
||||
},
|
||||
};
|
||||
}));
|
||||
|
||||
setParsingTemplate(false);
|
||||
setPendingMarkdown('');
|
||||
setEditingOutline(true);
|
||||
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
alert("模型解析遇到错误:" + (err.message || err));
|
||||
setParsingTemplate(false);
|
||||
setNewTemplatePending(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUploadClick = async () => {
|
||||
if (currentProject.activeTemplate.chapters && currentProject.activeTemplate.chapters.length > 0) {
|
||||
if (!window.confirm("当前已经存在解析好的交付标准目录,确定要重新上传并替换吗?已有的内容和结构将会被覆盖。")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const markdownContent = await ParseDeliveryStandard();
|
||||
if (markdownContent) {
|
||||
setPendingMarkdown(markdownContent);
|
||||
setNewTemplatePending(true);
|
||||
}
|
||||
} catch (error: any) {
|
||||
alert("读取或转换文件内容失败: " + (error.message || error));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-3 mx-2 my-4 mt-auto rounded-[14px] bg-[#F1EFEA]">
|
||||
<div className="flex items-center justify-between mb-3 text-[10px] font-medium text-[#8E8B83] uppercase tracking-[0.15em] px-1">
|
||||
<div className="flex items-center gap-2"><Shield size={12} className="text-[#2D2D2D]" /> 交付标准</div>
|
||||
<button
|
||||
onClick={handleUploadClick}
|
||||
className="p-1 hover:bg-[#EBE9E4] rounded-md text-[#8E8B83] hover:text-[#2D2D2D] transition-colors"
|
||||
title="上传交付标准"
|
||||
>
|
||||
<UploadCloud size={14} />
|
||||
</button>
|
||||
</div>
|
||||
<div className={`relative p-3 rounded-xl border transition-all duration-300 bg-[#FFFFFF] border-[#E5E4E0] shadow-[0_2px_10px_-4px_rgba(0,0,0,0.05)] ${
|
||||
isParsingTemplate ? 'border-[#E5E4E0] animate-pulse ring-1 ring-[#E5E4E0]' : ''
|
||||
}`}>
|
||||
{isParsingTemplate ? (
|
||||
<div className="flex flex-col items-center py-1 gap-2">
|
||||
<Loader2 size={16} className="text-[#D97757] animate-spin" />
|
||||
<span className="text-[9px] font-medium text-[#D97757] uppercase">AI 解析目录中...</span>
|
||||
</div>
|
||||
) : hasNewTemplatePending ? (
|
||||
<button
|
||||
onClick={startTemplateParse}
|
||||
className="w-full py-1.5 bg-[#D97757] text-white rounded-lg text-[11px] font-medium transition-all hover:bg-[#C86444] shadow-sm transform hover:scale-[1.01]"
|
||||
>
|
||||
开始大模型解析目录
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex items-start gap-3">
|
||||
<FileCheck size={16} className="text-[#8E8B83] mt-0.5" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-[12px] font-medium text-[#2D2D2D] truncate text-left">{currentProject.activeTemplate.name}</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="inline-flex items-center gap-1.5 text-[9px] font-mono text-[#8E8B83]">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-[#D97757]" />
|
||||
{currentProject.activeTemplate.version} ACTIVE
|
||||
</span>
|
||||
<RefreshCw size={10} className="text-[#C2C0B8]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user