+ lucide.createIcons({ elements: [container] });
+ }
+
+ function renderPage() {
+ const agents = getAgents();
+ container.innerHTML = `
+
- window.closeBC = (id, btn) => {
- const bc = mockCases.find(c => c.id === id);
- if (!bc) return;
- createModal({
- title: '关闭 Bad Case',
- content: `
-
`,
- onConfirm: () => {
- bc.status = 'closed';
- const row = btn?.closest('tr');
- const statusCell = row?.querySelector('td:nth-child(5)');
- if (statusCell) statusCell.innerHTML = '
';
- const actions = btn?.closest('div');
- if (actions) actions.innerHTML = `
`;
- lucide.createIcons();
- Toast.success(`${id} 已关闭`);
- },
- confirmText: '确认关闭',
- });
- };
+
- document.getElementById('manualBCBtn')?.addEventListener('click', () => {
- createModal({
+
+ `;
+
+ bindEvents();
+ renderResults();
+ }
+
+ function bindEvents() {
+ container.querySelector('#bcSearchInput')?.addEventListener('input', event => {
+ filters.keyword = event.target.value;
+ renderResults();
+ });
+
+ container.querySelector('#bcSeverityFilter')?.addEventListener('change', event => {
+ filters.severity = event.target.value;
+ renderResults();
+ });
+
+ container.querySelector('#bcStatusFilter')?.addEventListener('change', event => {
+ filters.status = event.target.value;
+ renderResults();
+ });
+
+ container.querySelector('#bcAgentFilter')?.addEventListener('change', event => {
+ filters.agent = event.target.value;
+ renderResults();
+ });
+
+ container.querySelector('#manualBCBtn')?.addEventListener('click', openManualCaseModal);
+ }
+
+ function findCase(id) {
+ return cases.find(item => item.id === id);
+ }
+
+ function getNextCaseId() {
+ const year = new Date().getFullYear();
+ const usedNumbers = cases
+ .map(item => String(item.id).match(/^BC-\d{4}-(\d+)$/))
+ .filter(Boolean)
+ .map(match => Number(match[1]));
+ const nextNumber = (usedNumbers.length ? Math.max(...usedNumbers) : 0) + 1;
+ return `BC-${year}-${String(nextNumber).padStart(3, '0')}`;
+ }
+
+ function openManualCaseModal() {
+ const agents = getAgents();
+ const modal = createModal({
title: '手动添加 Bad Case',
content: `
-
-
-
+
+
-
+
+
+
+ 后端接口预留:POST /api/v1/bad-cases 创建人工 Bad Case。
`,
onConfirm: async () => {
const issue = document.getElementById('manualBCIssue')?.value.trim();
@@ -265,9 +435,240 @@ registerPage('badcase', function(container) {
Toast.warning('请填写问题描述');
return false;
}
+
+ const selectedAgent = document.getElementById('manualBCAgent')?.value || '';
+ const customAgent = document.getElementById('manualBCCustomAgent')?.value.trim();
+ const agent = selectedAgent === 'custom' ? customAgent : selectedAgent;
+ if (!agent) {
+ Toast.warning('请填写智能体名称');
+ return false;
+ }
+
+ cases.unshift({
+ id: getNextCaseId(),
+ session: document.getElementById('manualBCSession')?.value.trim() || `sess-${Math.random().toString(16).slice(2, 6)}`,
+ device: document.getElementById('manualBCDevice')?.value.trim() || '手动录入',
+ agent,
+ issue,
+ severity: document.getElementById('manualBCSeverity')?.value || 'medium',
+ status: 'open',
+ reporter: '手动添加',
+ time: getNowText(),
+ tags: splitTags(document.getElementById('manualBCTags')?.value) || [],
+ dialogue: {
+ user: '手动添加案例,暂无原始用户消息',
+ assistant: issue,
+ },
+ });
+
+ try {
+ if (window.API?.badCases?.create) {
+ await API.badCases.create(cases[0]);
+ }
+ } catch (error) {
+ console.warn('Mock bad case create failed:', error);
+ }
+
+ filters.status = 'all';
+ renderPage();
Toast.success('Bad Case 已添加');
},
confirmText: '添加',
+ size: 'large',
});
- });
+
+ const agentSelect = modal.overlay.querySelector('#manualBCAgent');
+ const customWrap = modal.overlay.querySelector('#manualBCCustomWrap');
+ agentSelect?.addEventListener('change', event => {
+ if (customWrap) customWrap.style.display = event.target.value === 'custom' ? 'block' : 'none';
+ });
+ }
+
+ window.openBCDetail = async function(id) {
+ const bc = findCase(id);
+ if (!bc) {
+ Toast.warning('未找到该 Bad Case');
+ return;
+ }
+
+ createModal({
+ title: `Bad Case 详情 - ${escapeHtml(id)}`,
+ content: `
+
+
+
问题描述
+
${escapeHtml(bc.issue)}
+
+
+
+
+
关联对话
+
+
用户
+
${escapeHtml(bc.dialogue?.user || '暂无原始用户消息')}
+
AI 回复
+
${escapeHtml(bc.dialogue?.assistant || '暂无原始 AI 回复')}
+
+
+ ${bc.resolution ? `
+
+
最近处理意见
+
${escapeHtml(bc.resolution)}
+
+ ` : ''}
+
+
+
+
+
+
+
+
+
+
+ 后端接口预留:POST /api/v1/bad-cases/${escapeHtml(id)}/review 提交审核处理结果。
+
+
`,
+ onConfirm: async () => {
+ const resolution = document.getElementById('bcReviewResolution')?.value.trim();
+ const action = document.getElementById('bcReviewAction')?.value || 'none';
+ if (!resolution) {
+ Toast.warning('请填写处理意见');
+ return false;
+ }
+
+ try {
+ if (window.API?.badCases?.review) {
+ await API.badCases.review(id, { resolution, action });
+ }
+ } catch (error) {
+ console.warn('Mock bad case review failed:', error);
+ }
+
+ bc.resolution = resolution;
+ bc.reviewAction = action;
+ bc.updatedAt = getNowText();
+ if (bc.status !== 'closed') bc.status = 'reviewing';
+ renderResults();
+ Toast.success('已提交处理意见,状态已更新');
+ },
+ confirmText: '提交处理',
+ size: 'large',
+ });
+ };
+
+ window.openBCTagModal = function(id) {
+ const bc = findCase(id);
+ if (!bc) {
+ Toast.warning('未找到该 Bad Case');
+ return;
+ }
+
+ createModal({
+ title: `标注问题 - ${escapeHtml(id)}`,
+ content: `
+
+
+
+
+
+
+
+ 后端接口预留:PATCH /api/v1/bad-cases/${escapeHtml(id)}/labels 保存标签、严重度和备注。
+
`,
+ onConfirm: () => {
+ const tags = splitTags(document.getElementById('bcTagsInput')?.value);
+ if (!tags.length) {
+ Toast.warning('请填写至少一个问题标签');
+ return false;
+ }
+
+ bc.tags = tags;
+ bc.severity = document.getElementById('bcTagSeverity')?.value || bc.severity;
+ bc.tagNote = document.getElementById('bcTagNote')?.value.trim() || '';
+ bc.reporter = bc.reporter === '自动检测' ? '运营标注' : bc.reporter;
+ bc.updatedAt = getNowText();
+ if (bc.status === 'open') bc.status = 'reviewing';
+ renderResults();
+ Toast.success('问题标注已保存');
+ },
+ confirmText: '保存标注',
+ });
+ };
+
+ window.closeBC = function(id) {
+ const bc = findCase(id);
+ if (!bc) {
+ Toast.warning('未找到该 Bad Case');
+ return;
+ }
+
+ createModal({
+ title: '关闭 Bad Case',
+ content: `
+
确认关闭 ${escapeHtml(id)}?关闭后仍可查看详情,但不再进入待处理列表。
+
+
+
+
+
+
+ 后端接口预留:POST /api/v1/bad-cases/${escapeHtml(id)}/close 关闭问题案例。
+
`,
+ onConfirm: () => {
+ const reason = document.getElementById('bcCloseReason')?.value.trim();
+ if (!reason) {
+ Toast.warning('请填写关闭原因');
+ return false;
+ }
+
+ bc.status = 'closed';
+ bc.closedReason = reason;
+ bc.closedAt = getNowText();
+ renderResults();
+ Toast.success(`${id} 已关闭`);
+ },
+ confirmText: '确认关闭',
+ });
+ };
+
+ renderPage();
});
diff --git a/scripts/kb-create.js b/scripts/kb-create.js
index 9f3caa6..23d25f6 100644
--- a/scripts/kb-create.js
+++ b/scripts/kb-create.js
@@ -1173,8 +1173,39 @@ registerPage('kb-create', function(container) {
};
window.__finishWizard = function() {
+ collectStepData();
+
// TODO: 调用 API POST /api/v1/knowledge-bases 创建知识库
console.log('创建知识库数据:', formData);
+
+ const typeMap = {
+ doc_search: '文档搜索',
+ data_query: '数据查询',
+ image_qa: '图片问答',
+ video_search: '音视频搜索',
+ };
+ const createdAt = new Date();
+ const pad = value => String(value).padStart(2, '0');
+ const createdText = `${createdAt.getFullYear()}-${pad(createdAt.getMonth() + 1)}-${pad(createdAt.getDate())} ${pad(createdAt.getHours())}:${pad(createdAt.getMinutes())}:${pad(createdAt.getSeconds())}`;
+ const uploadedCount = Array.isArray(formData.uploadedFiles) ? formData.uploadedFiles.length : 0;
+ const newKb = {
+ id: `kb-${Date.now()}`,
+ name: formData.name || '未命名知识库',
+ description: formData.description || '暂无描述',
+ docs: uploadedCount * 24,
+ size: uploadedCount ? `${Math.max(1, uploadedCount * 8)}MB` : '0MB',
+ type: formData.type || 'doc_search',
+ typeName: typeMap[formData.type] || '文档搜索',
+ status: uploadedCount ? 'processing' : 'ready',
+ createdAt: createdText,
+ updatedAt: createdText,
+ tags: [],
+ };
+ const catalog = Array.isArray(window.__knowledgeBaseCatalog)
+ ? window.__knowledgeBaseCatalog
+ : [];
+ catalog.unshift(newKb);
+ window.__knowledgeBaseCatalog = catalog;
clearDraft();
Toast.success('知识库创建成功!');
@@ -1302,8 +1333,18 @@ registerPage('kb-create', function(container) {
window.__retryUpload = function(index) {
if (formData.uploadedFiles[index]) {
- formData.uploadedFiles[index].status = 'uploading';
+ const fileObj = formData.uploadedFiles[index];
+ fileObj.status = 'uploading';
renderPage();
+ setTimeout(() => {
+ fileObj.status = 'processing';
+ renderPage();
+ }, 500);
+ setTimeout(() => {
+ fileObj.status = 'ready';
+ renderPage();
+ Toast.success(`${fileObj.name} 已重新上传并解析完成`);
+ }, 1500);
}
};
@@ -1422,11 +1463,101 @@ registerPage('kb-create', function(container) {
window.__loadCategoryFiles = function(category) {
// TODO: 调用 API GET /api/v1/categories/:id/files
- Toast.info(`加载 ${category} 文件列表`);
+ createModal({
+ title: `${category} 文件列表`,
+ size: 'lg',
+ content: `
+
+ ${['儿童科学百科.pdf', '科学小实验.docx', '物理入门.xlsx'].map((name, index) => `
+
+
+
${name}
+
mock-file-${index + 1} · ${(index + 1) * 2}.4MB
+
+
+
+ `).join('')}
+
+ `,
+ confirmText: '',
+ cancelText: '关闭'
+ });
};
window.__showCategoryDrawer = function() {
- Toast.info('类目管理功能');
+ const renderCategoryRows = () => {
+ const list = document.getElementById('categoryManageList');
+ if (!list) return;
+ list.innerHTML = categories.map(category => `
+
+
+
${category.name}
+
${category.fileCount} 个文件 · ${category.id}
+
+
+
+ ${category.id === 'cat-001' ? '' : `
+
+ `}
+
+
+ `).join('');
+ lucide.createIcons({ elements: list });
+ };
+
+ createModal({
+ title: '类目管理',
+ size: 'lg',
+ content: `
+
+
+
+
+
+ `,
+ confirmText: '',
+ cancelText: '关闭'
+ });
+
+ window.__addCategoryFromModal = function() {
+ const input = document.getElementById('newCategoryName');
+ const name = input?.value.trim();
+ if (!name) {
+ Toast.warning('请输入类目名称');
+ return;
+ }
+ categories.push({
+ id: `cat-${Date.now()}`,
+ name,
+ fileCount: 0,
+ });
+ input.value = '';
+ renderCategoryRows();
+ Toast.success('类目已添加');
+ };
+
+ window.__removeCategoryFromModal = function(id) {
+ const index = categories.findIndex(category => category.id === id);
+ if (index !== -1) {
+ categories.splice(index, 1);
+ renderCategoryRows();
+ Toast.success('类目已删除');
+ }
+ };
+
+ window.__chooseCategoryFromModal = function(name) {
+ formData.category = name;
+ hasUnsavedChanges = true;
+ const select = document.getElementById('categorySelect');
+ if (select && Array.from(select.options).some(option => option.value === name)) {
+ select.value = name;
+ }
+ Toast.success(`已选择类目:${name}`);
+ };
+
+ setTimeout(renderCategoryRows, 50);
};
window.__saveDraft = saveDraft;
diff --git a/scripts/kb-detail.js b/scripts/kb-detail.js
index 46c228c..8327c5e 100644
--- a/scripts/kb-detail.js
+++ b/scripts/kb-detail.js
@@ -199,6 +199,14 @@ registerPage('kb-detail', function(container) {
return timeStr;
}
+ function escapeHtml(str) {
+ return String(str || '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+ }
+
function getStatusBadge(status) {
const map = {
ready: { label: '解析完成', cls: 'badge-success' },
@@ -209,6 +217,31 @@ registerPage('kb-detail', function(container) {
return map[status] || { label: status, cls: 'badge-default' };
}
+ function getNowText() {
+ const date = new Date();
+ const pad = value => String(value).padStart(2, '0');
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
+ }
+
+ function formatBytes(bytes) {
+ const value = Number(bytes) || 0;
+ if (value < 1024) return `${value}B`;
+ if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)}KB`;
+ return `${(value / 1024 / 1024).toFixed(1)}MB`;
+ }
+
+ function downloadTextFile(filename, content, mime = 'application/json;charset=utf-8') {
+ const blob = new Blob([content], { type: mime });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ link.remove();
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
+ }
+
// ==================== 渲染函数 ====================
function renderPage() {
@@ -759,12 +792,66 @@ registerPage('kb-detail', function(container) {
};
window.__filterSlices = function(keyword) {
- Toast.info('搜索切片: ' + keyword);
+ const query = String(keyword || '').trim().toLowerCase();
+ document.querySelectorAll('#sliceList .slice-list-item').forEach(item => {
+ const text = item.textContent.toLowerCase();
+ item.style.display = text.includes(query) ? '' : 'none';
+ });
};
// 文件操作
window.__uploadFiles = function() {
- Toast.info('上传文件功能');
+ createModal({
+ title: '上传文件',
+ size: 'lg',
+ content: `
+
+
+
+
+
+ `,
+ confirmText: '上传',
+ onConfirm: () => {
+ const input = document.getElementById('detailUploadInput');
+ const files = Array.from(input?.files || []);
+ if (!files.length) {
+ Toast.warning('请先选择文件');
+ return false;
+ }
+ const category = document.getElementById('detailUploadCategory')?.value || '默认类目';
+ files.forEach(file => {
+ const ext = file.name.split('.').pop()?.toUpperCase() || 'FILE';
+ kbFiles.unshift({
+ id: `f-${Date.now()}-${Math.random().toString(16).slice(2, 7)}`,
+ name: file.name,
+ size: formatBytes(file.size),
+ format: ext,
+ dataSize: formatBytes(file.size),
+ status: 'processing',
+ category,
+ indexedAt: '-',
+ slices: 0,
+ chars: 0,
+ parseError: null,
+ });
+ });
+ kbData.docs += files.length;
+ kbData.updatedAt = getNowText();
+ selectedFiles.clear();
+ renderPage();
+ Toast.success(`已上传 ${files.length} 个文件`);
+ }
+ });
};
window.__viewMetaInfo = function(fileId) {
@@ -848,7 +935,31 @@ registerPage('kb-detail', function(container) {
};
window.__moveFile = function(fileId) {
- Toast.info('移动文件功能');
+ const file = kbFiles.find(f => f.id === fileId);
+ if (!file) {
+ Toast.warning('未找到文件');
+ return;
+ }
+ createModal({
+ title: '移动到类目',
+ content: `
+
文件:${file.name}
+
+
+
+
+ `,
+ confirmText: '移动',
+ onConfirm: () => {
+ file.category = document.getElementById('moveFileCategory')?.value || file.category;
+ renderPage();
+ Toast.success('文件已移动');
+ }
+ });
};
window.__deleteFile = function(fileId) {
@@ -875,6 +986,10 @@ registerPage('kb-detail', function(container) {
// 批量操作
window.__reparseSelectedFiles = function() {
+ if (selectedFiles.size === 0) {
+ Toast.warning('请先选择文件');
+ return;
+ }
createModal({
title: '重新解析',
content: `
确定要重新解析选中的 ${selectedFiles.size} 个文件吗?
`,
@@ -893,10 +1008,43 @@ registerPage('kb-detail', function(container) {
};
window.__moveSelectedFiles = function() {
- Toast.info('移动文件功能');
+ if (selectedFiles.size === 0) {
+ Toast.warning('请先选择文件');
+ return;
+ }
+ createModal({
+ title: '批量移动类目',
+ content: `
+
将选中的 ${selectedFiles.size} 个文件移动到:
+
+
+
+
+ `,
+ confirmText: '移动',
+ onConfirm: () => {
+ const category = document.getElementById('moveSelectedCategory')?.value || '默认类目';
+ selectedFiles.forEach(id => {
+ const file = kbFiles.find(f => f.id === id);
+ if (file) file.category = category;
+ });
+ selectedFiles.clear();
+ renderPage();
+ Toast.success('文件已移动');
+ }
+ });
};
window.__deleteSelectedFiles = function() {
+ if (selectedFiles.size === 0) {
+ Toast.warning('请先选择文件');
+ return;
+ }
createModal({
title: '批量删除',
content: `
@@ -1164,7 +1312,49 @@ registerPage('kb-detail', function(container) {
// 知识库操作
window.__editKb = function() {
- Toast.info('编辑知识库');
+ createModal({
+ title: '编辑知识库',
+ size: 'lg',
+ content: `
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ confirmText: '保存',
+ onConfirm: () => {
+ const name = document.getElementById('detailKbName')?.value.trim();
+ if (!name) {
+ Toast.warning('请输入知识库名称');
+ return false;
+ }
+ kbData.name = name;
+ kbData.description = document.getElementById('detailKbDesc')?.value.trim() || '';
+ kbData.tags = (document.getElementById('detailKbTags')?.value || '')
+ .split(/[,,]/)
+ .map(tag => tag.trim())
+ .filter(Boolean);
+ kbData.updatedAt = getNowText();
+ const catalog = Array.isArray(window.__knowledgeBaseCatalog) ? window.__knowledgeBaseCatalog : [];
+ const target = catalog.find(item => item.id === kbData.id);
+ if (target) Object.assign(target, {
+ name: kbData.name,
+ description: kbData.description,
+ tags: [...kbData.tags],
+ updatedAt: kbData.updatedAt,
+ });
+ renderPage();
+ Toast.success('知识库信息已更新');
+ }
+ });
};
window.__rebuildAllIndex = function() {
@@ -1177,6 +1367,15 @@ registerPage('kb-detail', function(container) {
confirmText: '重建',
onConfirm: () => {
// TODO: 调用 API POST /api/v1/knowledge-bases/:id/rebuild
+ kbData.status = 'processing';
+ kbData.updatedAt = getNowText();
+ const catalog = Array.isArray(window.__knowledgeBaseCatalog) ? window.__knowledgeBaseCatalog : [];
+ const target = catalog.find(item => item.id === kbData.id);
+ if (target) {
+ target.status = kbData.status;
+ target.updatedAt = kbData.updatedAt;
+ }
+ renderPage();
Toast.success('索引重建任务已启动');
}
});
@@ -1191,13 +1390,75 @@ registerPage('kb-detail', function(container) {
`,
confirmText: '重新解析',
onConfirm: () => {
+ kbFiles = kbFiles.map(file => ({
+ ...file,
+ status: 'processing',
+ indexedAt: '-',
+ parseError: null,
+ }));
+ kbData.status = 'processing';
+ kbData.updatedAt = getNowText();
+ const catalog = Array.isArray(window.__knowledgeBaseCatalog) ? window.__knowledgeBaseCatalog : [];
+ const target = catalog.find(item => item.id === kbData.id);
+ if (target) {
+ target.status = kbData.status;
+ target.updatedAt = kbData.updatedAt;
+ }
+ selectedFiles.clear();
+ renderPage();
Toast.success('重新解析任务已启动');
}
});
};
window.__exportKb = function() {
- Toast.info('导出知识库');
+ createModal({
+ title: '导出知识库',
+ content: `
+
+
+
+
+
+
浏览器会下载到默认下载目录;后端接入后替换为 GET /api/v1/knowledge-bases/:id/export。
+ `,
+ confirmText: '确认导出',
+ onConfirm: () => {
+ const format = document.getElementById('detailExportFormat')?.value || 'json';
+ const includeFiles = document.getElementById('detailExportIncludeFiles')?.checked !== false;
+ const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
+ if (format === 'csv') {
+ const rows = [
+ 'id,name,format,size,status,category,slices,chars,indexedAt',
+ ...kbFiles.map(file => [
+ file.id,
+ file.name,
+ file.format,
+ file.size,
+ file.status,
+ file.category,
+ file.slices,
+ file.chars,
+ file.indexedAt,
+ ].map(value => `"${String(value ?? '').replace(/"/g, '""')}"`).join(',')),
+ ].join('\n');
+ downloadTextFile(`${kbData.id}-files-${stamp}.csv`, rows, 'text/csv;charset=utf-8');
+ } else {
+ downloadTextFile(`${kbData.id}-${stamp}.json`, JSON.stringify({
+ knowledgeBase: kbData,
+ files: includeFiles ? kbFiles : undefined,
+ slices: includeFiles ? mockSlices : undefined,
+ }, null, 2));
+ }
+ Toast.success('知识库已导出');
+ }
+ });
};
window.__deleteKb = function() {
@@ -1217,6 +1478,10 @@ registerPage('kb-detail', function(container) {
confirmClass: 'btn-danger',
onConfirm: () => {
// TODO: 调用 API DELETE /api/v1/knowledge-bases/:id
+ if (Array.isArray(window.__knowledgeBaseCatalog)) {
+ const index = window.__knowledgeBaseCatalog.findIndex(item => item.id === kbData.id);
+ if (index !== -1) window.__knowledgeBaseCatalog.splice(index, 1);
+ }
Toast.success('知识库已删除');
setTimeout(() => navigateTo('knowledge-base'), 1000);
}
diff --git a/scripts/knowledge-base.js b/scripts/knowledge-base.js
index e306313..1493cf0 100644
--- a/scripts/knowledge-base.js
+++ b/scripts/knowledge-base.js
@@ -19,6 +19,7 @@ registerPage('knowledge-base', function(container) {
let sortField = 'updatedAt';
let sortOrder = 'desc';
let isLoading = true;
+ let hasStartedInitialLoad = false;
let kbData = [];
// ==================== API预留 - 后端接口定义 ====================
@@ -71,8 +72,37 @@ registerPage('knowledge-base', function(container) {
{ id: 'kb-008', name: '音乐教育资源库', description: '儿童音乐教育资源', docs: 560, size: '65MB', type: 'video_search', typeName: '音视频搜索', status: 'uploading', createdAt: '2026-05-12 10:00:00', updatedAt: '2026-05-12 18:00:00', tags: ['音乐', '教育'] },
];
- // 初始化数据
- kbData = [...mockKbData];
+ // 初始化数据。页面刷新后回到 mock;SPA 内新建/编辑/删除会保留在全局目录中。
+ kbData = Array.isArray(window.__knowledgeBaseCatalog) && window.__knowledgeBaseCatalog.length
+ ? window.__knowledgeBaseCatalog.map(item => ({ ...item, tags: [...(item.tags || [])] }))
+ : mockKbData.map(item => ({ ...item, tags: [...(item.tags || [])] }));
+ window.__knowledgeBaseCatalog = kbData;
+
+ function syncKnowledgeBaseCatalog() {
+ window.__knowledgeBaseCatalog = kbData;
+ }
+
+ function getKbById(id) {
+ return kbData.find(kb => kb.id === id);
+ }
+
+ function getNowText() {
+ const date = new Date();
+ const pad = value => String(value).padStart(2, '0');
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
+ }
+
+ function downloadTextFile(filename, content, mime = 'application/json;charset=utf-8') {
+ const blob = new Blob([content], { type: mime });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ link.remove();
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
+ }
// ==================== 工具函数 ====================
@@ -197,6 +227,13 @@ registerPage('knowledge-base', function(container) {
if (sortField === 'name' || sortField === 'typeName') {
return sortOrder === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA);
}
+ if (sortField === 'createdAt' || sortField === 'updatedAt') {
+ valA = Date.parse(valA || '') || 0;
+ valB = Date.parse(valB || '') || 0;
+ } else {
+ valA = Number(valA) || 0;
+ valB = Number(valB) || 0;
+ }
return sortOrder === 'asc' ? valA - valB : valB - valA;
});
@@ -252,7 +289,7 @@ registerPage('knowledge-base', function(container) {