Compare commits

..

No commits in common. "1977477fd3cbe48d16efc4c3780be6fd9c20fd80" and "39be510299539e37c539bb1c58e8654817515c79" have entirely different histories.

7 changed files with 87 additions and 256 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
dist.zip

Binary file not shown.

View File

@ -144,11 +144,11 @@ const MindMap = ({ content, title }) => {
try { try {
const processedMarkdown = preprocessMarkdownForMindMap(markdown, title); const processedMarkdown = preprocessMarkdownForMindMap(markdown, title);
// console.log('=== ==='); console.log('=== 思维导图数据调试 ===');
// console.log('markdown:'); console.log('原始markdown内容:');
// console.log(markdown); console.log(markdown);
// console.log('markdown:'); console.log('预处理后的markdown:');
// console.log(processedMarkdown); console.log(processedMarkdown);
const transformer = new Transformer(); const transformer = new Transformer();
const { root } = transformer.transform(processedMarkdown); const { root } = transformer.transform(processedMarkdown);
@ -161,20 +161,10 @@ const MindMap = ({ content, title }) => {
markmapRef.current.fit(); markmapRef.current.fit();
// fit // fit
setTimeout(() => { setTimeout(() => {
if (markmapRef.current) { if (markmapRef.current) {
markmapRef.current.fit(); markmapRef.current.fit();
// fit1.8
try {
const { state } = markmapRef.current;
if (state) {
const currentScale = state.transform.k;
markmapRef.current.rescale(currentScale * 1.8);
}
} catch (e) {
console.log('缩放调整失败:', e);
}
} }
}, 500); }, 500);

View File

@ -504,7 +504,6 @@ const EditMeeting = ({ user }) => {
onClick={() => { onClick={() => {
setShowUploadArea(false); setShowUploadArea(false);
setAudioFile(null); setAudioFile(null);
setError('');
// Reset file input // Reset file input
const fileInput = document.getElementById('audio-file'); const fileInput = document.getElementById('audio-file');
if (fileInput) fileInput.value = ''; if (fileInput) fileInput.value = '';
@ -551,36 +550,6 @@ const EditMeeting = ({ user }) => {
)} )}
</div> </div>
)} )}
{/* Error message for audio upload - shown right after upload area */}
{error && showUploadArea && (
<div className="error-message">{error}</div>
)}
{/* Upload Confirmation Modal - moved here to be right after upload area */}
{showUploadConfirm && (
<div className="delete-modal-overlay" onClick={() => setShowUploadConfirm(false)}>
<div className="delete-modal" onClick={(e) => e.stopPropagation()}>
<h3>确认重新上传</h3>
<p>重传音频文件将清空已有的会话转录是否继续</p>
<div className="modal-actions">
<button
className="btn-cancel"
onClick={() => setShowUploadConfirm(false)}
>
取消
</button>
<button
className="btn-submit"
onClick={handleUploadAudio}
disabled={isUploading}
>
确定重传
</button>
</div>
</div>
</div>
)}
</div> </div>
</div> </div>
@ -635,7 +604,7 @@ const EditMeeting = ({ user }) => {
)} )}
</div> </div>
{error && !showUploadArea && ( {error && (
<div className="error-message">{error}</div> <div className="error-message">{error}</div>
)} )}
@ -655,6 +624,31 @@ const EditMeeting = ({ user }) => {
</form> </form>
</div> </div>
</div> </div>
{/* Upload Confirmation Modal */}
{showUploadConfirm && (
<div className="delete-modal-overlay" onClick={() => setShowUploadConfirm(false)}>
<div className="delete-modal" onClick={(e) => e.stopPropagation()}>
<h3>确认重新上传</h3>
<p>重传音频文件将清空已有的会话转录是否继续</p>
<div className="modal-actions">
<button
className="btn-cancel"
onClick={() => setShowUploadConfirm(false)}
>
取消
</button>
<button
className="btn-submit"
onClick={handleUploadAudio}
disabled={isUploading}
>
确定重传
</button>
</div>
</div>
</div>
)}
</div> </div>
); );
}; };

View File

@ -93,6 +93,8 @@
padding: 0.5rem; padding: 0.5rem;
border: none; border: none;
border-radius: 6px; border-radius: 6px;
background: #f1f5f9;
color: #64748b;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
display: flex; display: flex;
@ -100,20 +102,9 @@
justify-content: center; justify-content: center;
} }
.btn-new-kb {
background: #667eea;
color: white;
}
.btn-new-kb:hover { .btn-new-kb:hover {
background: #5568d3; background: #e2e8f0;
transform: translateY(-1px); color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.btn-toggle-sidebar {
background: #f1f5f9;
color: #64748b;
} }
.btn-toggle-sidebar:hover { .btn-toggle-sidebar:hover {
@ -128,27 +119,6 @@
padding: 0.5rem; padding: 0.5rem;
} }
/* 日期分组 */
.kb-date-group {
margin-bottom: 1rem;
}
.date-group-header {
padding: 0.5rem 0.75rem;
margin: 0.5rem 0;
font-size: 0.75rem;
font-weight: 600;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid #e2e8f0;
background: #f8fafc;
border-radius: 6px;
position: sticky;
top: 0;
z-index: 10;
}
.kb-list-item { .kb-list-item {
padding: 1rem; padding: 1rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
@ -657,25 +627,13 @@
width: 18px; width: 18px;
height: 18px; height: 18px;
cursor: pointer; cursor: pointer;
flex-shrink: 0;
} }
.meeting-item-content { .meeting-item label {
cursor: pointer;
flex: 1; flex: 1;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.meeting-item-title {
font-size: 0.95rem; font-size: 0.95rem;
color: #475569; color: #475569;
font-weight: 500;
}
.meeting-item-creator {
font-size: 0.8rem;
color: #94a3b8;
} }
.selected-meetings-info { .selected-meetings-info {

View File

@ -74,14 +74,10 @@ const KnowledgeBasePage = ({ user }) => {
// //
apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.LIST)) apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.LIST))
.then(response => { .then(response => {
// setKbs(response.data.kbs);
const sortedKbs = response.data.kbs.sort((a, b) =>
new Date(b.created_at) - new Date(a.created_at)
);
setKbs(sortedKbs);
// //
if (sortedKbs.length > 0 && !selectedKb) { if (response.data.kbs.length > 0 && !selectedKb) {
loadKbDetail(sortedKbs[0].kb_id); loadKbDetail(response.data.kbs[0].kb_id);
} }
setLoading(false); setLoading(false);
}) })
@ -181,45 +177,6 @@ const KnowledgeBasePage = ({ user }) => {
}); });
}; };
const formatTime = (dateString) => {
const date = new Date(dateString);
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
};
const formatShortDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN', {
month: 'numeric',
day: 'numeric'
});
};
const isToday = (dateString) => {
const date = new Date(dateString);
const today = new Date();
return date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear();
};
const groupKbsByDate = (kbList) => {
const todayKbs = [];
const pastKbs = [];
kbList.forEach(kb => {
if (isToday(kb.created_at)) {
todayKbs.push(kb);
} else {
pastKbs.push(kb);
}
});
return { todayKbs, pastKbs };
};
const handleLogoClick = () => { const handleLogoClick = () => {
navigate('/dashboard'); navigate('/dashboard');
}; };
@ -488,110 +445,51 @@ const KnowledgeBasePage = ({ user }) => {
<p>暂无知识库条目</p> <p>暂无知识库条目</p>
</div> </div>
) : ( ) : (
(() => { kbs.map(kb => (
const { todayKbs, pastKbs } = groupKbsByDate(kbs); <div
return ( key={kb.kb_id}
<> className={`kb-list-item ${selectedKb && selectedKb.kb_id === kb.kb_id ? 'active' : ''}`}
{/* 今天的知识库 */} onClick={() => handleKbSelect(kb)}
{todayKbs.length > 0 && ( >
<div className="kb-date-group"> <div className="kb-list-item-header">
<div className="date-group-header">今天</div> <h3>{kb.title}</h3>
{todayKbs.map(kb => ( {isCreator && (
<div <div className="kb-item-actions">
key={kb.kb_id} <button
className={`kb-list-item ${selectedKb && selectedKb.kb_id === kb.kb_id ? 'active' : ''}`} className="btn-edit-kb"
onClick={() => handleKbSelect(kb)} onClick={(e) => {
> e.stopPropagation();
<div className="kb-list-item-header"> navigate(`/knowledge-base/edit/${kb.kb_id}`);
<h3>{kb.title}</h3> }}
{String(kb.creator_id) === String(user.user_id) && ( title="编辑"
<div className="kb-item-actions"> >
<button <Edit size={14} />
className="btn-edit-kb" </button>
onClick={(e) => { <button
e.stopPropagation(); className="btn-delete-kb"
navigate(`/knowledge-base/edit/${kb.kb_id}`); onClick={(e) => {
}} e.stopPropagation();
title="编辑" handleDelete(kb);
> }}
<Edit size={14} /> title="删除"
</button> >
<button <Trash2 size={14} />
className="btn-delete-kb" </button>
onClick={(e) => {
e.stopPropagation();
handleDelete(kb);
}}
title="删除"
>
<Trash2 size={14} />
</button>
</div>
)}
</div>
<div className="kb-list-item-meta">
<span className="meta-time">{formatTime(kb.created_at)}</span>
<span className="meta-item">
<Database size={12} />
{kb.source_meeting_count || 0} 个数据源
</span>
</div>
</div>
))}
</div> </div>
)} )}
</div>
{/* 之前的知识库 */} <div className="kb-list-item-meta">
{pastKbs.length > 0 && ( <span className="meta-item">
<div className="kb-date-group"> <Calendar size={12} />
<div className="date-group-header">之前</div> {formatDate(kb.created_at)}
{pastKbs.map(kb => ( </span>
<div <span className="meta-item">
key={kb.kb_id} <Database size={12} />
className={`kb-list-item ${selectedKb && selectedKb.kb_id === kb.kb_id ? 'active' : ''}`} {kb.source_meeting_count || 0} 个数据源
onClick={() => handleKbSelect(kb)} </span>
> </div>
<div className="kb-list-item-header"> </div>
<h3>{kb.title}</h3> ))
{String(kb.creator_id) === String(user.user_id) && (
<div className="kb-item-actions">
<button
className="btn-edit-kb"
onClick={(e) => {
e.stopPropagation();
navigate(`/knowledge-base/edit/${kb.kb_id}`);
}}
title="编辑"
>
<Edit size={14} />
</button>
<button
className="btn-delete-kb"
onClick={(e) => {
e.stopPropagation();
handleDelete(kb);
}}
title="删除"
>
<Trash2 size={14} />
</button>
</div>
)}
</div>
<div className="kb-list-item-meta">
<span className="meta-date">{formatShortDate(kb.created_at)}</span>
<span className="meta-item">
<Database size={12} />
{kb.source_meeting_count || 0} 个数据源
</span>
</div>
</div>
))}
</div>
)}
</>
);
})()
)} )}
</div> </div>
)} )}
@ -746,18 +644,9 @@ const KnowledgeBasePage = ({ user }) => {
<input <input
type="checkbox" type="checkbox"
checked={selectedMeetings.includes(meeting.meeting_id)} checked={selectedMeetings.includes(meeting.meeting_id)}
onChange={(e) => { onChange={() => toggleMeetingSelection(meeting.meeting_id)}
e.stopPropagation();
toggleMeetingSelection(meeting.meeting_id);
}}
onClick={(e) => e.stopPropagation()}
/> />
<div className="meeting-item-content"> <label>{meeting.title}</label>
<div className="meeting-item-title">{meeting.title}</div>
{meeting.creator_username && (
<div className="meeting-item-creator">创建人: {meeting.creator_username}</div>
)}
</div>
</div> </div>
)) ))
)} )}

View File

@ -6,7 +6,7 @@ import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw'; import rehypeRaw from 'rehype-raw';
import rehypeSanitize from 'rehype-sanitize'; import rehypeSanitize from 'rehype-sanitize';
import { buildApiUrl, API_ENDPOINTS, API_BASE_URL } from '../config/api'; import { buildApiUrl, API_ENDPOINTS } from '../config/api';
import ContentViewer from '../components/ContentViewer'; import ContentViewer from '../components/ContentViewer';
import TagDisplay from '../components/TagDisplay'; import TagDisplay from '../components/TagDisplay';
import { Tabs } from 'antd'; import { Tabs } from 'antd';