main
HuangHai 1 month ago
parent cddd25716d
commit ab632ab395

@ -8,12 +8,11 @@ class KbDao:
self.logger = logging.getLogger(__name__)
async def create_kb(self, kb: Dict) -> int:
"""创建知识库"""
async with self.mysql_pool.acquire() as conn:
async with conn.cursor(DictCursor) as cur:
await cur.execute(
"INSERT INTO t_ai_kb(name, description) VALUES(%s, %s)",
(kb['name'], kb['description']))
"INSERT INTO t_ai_kb(kb_name, short_name) VALUES(%s, %s)",
(kb['name'], kb.get('description', '')))
await conn.commit()
return cur.lastrowid

@ -3,100 +3,126 @@
<head>
<meta charset="UTF-8">
<title>知识库管理系统</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcdn.net/ajax/libs/bootstrap-icons/1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 侧边栏 -->
<div class="col-md-3 col-lg-2 d-md-block bg-dark sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#kb-list" data-bs-toggle="tab">
<i class="bi bi-book"></i> 知识库列表
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#file-list" data-bs-toggle="tab">
<i class="bi bi-file-earmark"></i> 文件管理
</a>
</li>
</ul>
</div>
<div class="container-fluid">
<div class="row">
<!-- 侧边栏 -->
<div class="col-md-3 col-lg-2 d-md-block bg-dark sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#kb-list" data-bs-toggle="tab">
<i class="bi bi-book"></i> 知识库列表
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#file-list" data-bs-toggle="tab">
<i class="bi bi-file-earmark"></i> 文件管理
</a>
</li>
</ul>
</div>
</div>
<!-- 主内容区 -->
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div class="tab-content">
<!-- 知识库管理 -->
<div class="tab-pane fade show active" id="kb-list">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">知识库管理</h1>
<button class="btn btn-primary" id="add-kb-btn">添加知识库</button>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover" id="kb-table">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>描述</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="kb-table-body"></tbody>
</table>
</div>
<!-- 主内容区 -->
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div class="tab-content">
<!-- 知识库管理 -->
<div class="tab-pane fade show active" id="kb-list">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">知识库管理</h1>
<button class="btn btn-primary" id="add-kb-btn">添加知识库</button>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover" id="kb-table">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>描述</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="kb-table-body"></tbody>
</table>
</div>
</div>
<!-- 文件管理 -->
<div class="tab-pane fade" id="file-list">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">文件管理</h1>
<button class="btn btn-primary" id="upload-file-btn">上传文件</button>
</div>
<div class="mb-3">
<select class="form-select" id="kb-selector">
<option value="">-- 选择知识库 --</option>
</select>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover" id="file-table">
<thead>
<tr>
<th>ID</th>
<th>文件名</th>
<th>大小</th>
<th>类型</th>
<th>状态</th>
<th>上传时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="file-table-body"></tbody>
</table>
</div>
<!-- 文件管理 -->
<div class="tab-pane fade" id="file-list">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">文件管理</h1>
<button class="btn btn-primary" id="upload-file-btn">上传文件</button>
</div>
<div class="mb-3">
<select class="form-select" id="kb-selector">
<option value="">-- 选择知识库 --</option>
</select>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover" id="file-table">
<thead>
<tr>
<th>ID</th>
<th>文件名</th>
<th>大小</th>
<th>类型</th>
<th>状态</th>
<th>上传时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="file-table-body"></tbody>
</table>
</div>
</div>
</main>
</div>
</div>
</main>
</div>
<!-- 模态框 -->
<div class="modal fade" id="kb-modal" tabindex="-1" aria-hidden="true">
<!-- 知识库添加/编辑模态框内容 -->
</div>
<div id="kb-list-container" class="mt-3">
<!-- 知识库列表将在这里动态生成 -->
</div>
<!-- 模态框 -->
<div class="modal fade" id="kb-modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="kb-modal-title">添加知识库</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="kb-form">
<div class="mb-3">
<label for="kb-name" class="form-label">知识库名称</label>
<input type="text" class="form-control" id="kb-name" name="name" required>
</div>
<div class="mb-3">
<label for="kb-desc" class="form-label">描述</label>
<textarea class="form-control" id="kb-desc" name="description" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" form="kb-form" class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="file-modal" tabindex="-1" aria-hidden="true">
<!-- 文件上传模态框内容 -->
</div>
<div class="modal fade" id="file-modal" tabindex="-1" aria-hidden="true">
<!-- 文件上传模态框内容 -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.1/axios.min.js"></script>
<script src="js/app.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.1/axios.min.js"></script>
<script src="js/app.js"></script>
</body>
</html>

@ -1,139 +1,318 @@
// API基础URL
const API_BASE_URL = 'http://localhost:8000';
// 初始化页面
// 增强错误处理
window.onerror = function(message, source, lineno, colno, error) {
console.error('全局错误:', {message, source, lineno, colno, error});
alert('系统错误: ' + message);
return true;
};
// 修改初始化代码
document.addEventListener('DOMContentLoaded', function() {
loadKbList();
setupEventListeners();
try {
// 检查API是否可用
axios.get('/kb')
.then(response => {
if (response.status === 200) {
initApp();
} else {
throw new Error('API不可用');
}
})
.catch(error => {
console.error('API检查失败:', error);
if(error.response) {
console.log('HTTP状态码:', error.response.status);
console.log('响应数据:', error.response.data);
}
alert(`后端服务异常: ${error.message || '未知错误'}`);
});
} catch (error) {
console.error('初始化异常:', error);
alert('初始化失败: ' + error.message);
}
});
// 设置事件监听器
function setupEventListeners() {
// 添加知识库按钮
document.getElementById('add-kb-btn').addEventListener('click', showKbModal);
// 上传文件按钮
document.getElementById('upload-file-btn').addEventListener('click', showFileModal);
// 知识库选择器变化
document.getElementById('kb-selector').addEventListener('change', function() {
loadFileList(this.value);
});
}
// 加载知识库列表
async function loadKbList() {
$(document).ready(function() {
try {
const response = await axios.get(`${API_BASE_URL}/kb`);
const kbList = response.data;
const tbody = document.getElementById('kb-table-body');
tbody.innerHTML = '';
const kbSelector = document.getElementById('kb-selector');
kbSelector.innerHTML = '<option value="">-- 选择知识库 --</option>';
initApp();
} catch (e) {
console.error('初始化失败:', e);
alert('系统初始化失败: ' + e.message);
}
});
$(function() {
// 初始化应用
function initApp() {
loadKbList();
kbList.forEach(kb => {
// 填充知识库表格
const row = document.createElement('tr');
row.innerHTML = `
<td>${kb.id}</td>
<td>${kb.name}</td>
<td>${kb.description || '-'}</td>
<td>${new Date(kb.create_time).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-outline-primary edit-kb" data-id="${kb.id}">编辑</button>
<button class="btn btn-sm btn-outline-danger delete-kb" data-id="${kb.id}">删除</button>
</td>
`;
tbody.appendChild(row);
// 填充知识库选择器
const option = document.createElement('option');
option.value = kb.id;
option.textContent = kb.name;
kbSelector.appendChild(option);
// 绑定事件
$('#add-kb-btn').click(showKbModal);
$('#kb-selector').change(function() {
loadFileList($(this).val());
});
// 添加编辑和删除按钮的事件
document.querySelectorAll('.edit-kb').forEach(btn => {
btn.addEventListener('click', function() {
showKbModal(this.getAttribute('data-id'));
}
// 加载知识库列表
function loadKbList() {
$.get(API_BASE_URL + '/kb')
.done(function(data) {
const $container = $('#kb-table-body').empty();
$.each(data, function(index, kb) {
$container.append(`
<tr>
<td>${kb.id}</td>
<td>${kb.kb_name}</td>
<td>${kb.short_name || ''}</td>
<td>${new Date(kb.create_time).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-primary edit-kb" data-id="${kb.id}">编辑</button>
<button class="btn btn-sm btn-danger delete-kb" data-id="${kb.id}">删除</button>
</td>
</tr>
`);
});
bindKbActions();
})
.fail(function(error) {
console.error('加载知识库列表失败:', error);
alert('加载知识库列表失败');
});
}
// 绑定知识库操作
function bindKbActions() {
$(document).on('click', '.edit-kb', function() {
const kbId = $(this).data('id');
showKbModal(kbId);
});
document.querySelectorAll('.delete-kb').forEach(btn => {
btn.addEventListener('click', function() {
deleteKb(this.getAttribute('data-id'));
});
$(document).on('click', '.delete-kb', function() {
const kbId = $(this).data('id');
if(confirm('确定要删除这个知识库吗?')) {
deleteKb(kbId);
}
});
}
// 初始化应用
initApp();
});
// 添加调试日志
function showKbModal(kbId = null) {
console.log('显示知识库模态框ID:', kbId);
const modal = document.getElementById('kb-modal');
const form = document.getElementById('kb-form');
const title = document.getElementById('kb-modal-title');
// 重置表单
form.reset();
// 设置标题
title.textContent = kbId ? '编辑知识库' : '添加知识库';
// 如果是编辑模式,加载知识库数据
if (kbId) {
axios.get(`${API_BASE_URL}/kb/${kbId}`)
.then(response => {
const kb = response.data;
form.elements['name'].value = kb.name;
form.elements['description'].value = kb.description || '';
})
.catch(error => {
console.error('加载知识库详情失败:', error);
alert('加载知识库详情失败');
});
}
// 显示模态框
$(modal).modal('show');
// 处理表单提交
form.onsubmit = async function(e) {
e.preventDefault();
const formData = {
name: form.elements['name'].value,
description: form.elements['description'].value
};
try {
if (kbId) {
// 更新知识库
await axios.post(`${API_BASE_URL}/kb/update/${kbId}`, formData);
alert('更新成功');
} else {
// 创建知识库
await axios.post(`${API_BASE_URL}/kb`, formData);
alert('添加成功');
}
// 刷新列表并关闭模态框
loadKbList();
$(modal).modal('hide');
} catch (error) {
console.error('操作失败:', error);
alert('操作失败');
}
};
}
// 显示文件上传模态框
function showFileModal() {
// 实现文件上传模态框逻辑
}
// 删除知识库
async function deleteKb(kbId) {
if (!confirm('确定要删除这个知识库吗?')) return;
try {
await axios.delete(`${API_BASE_URL}/kb/${kbId}`);
alert('删除成功');
loadKbList();
} catch (error) {
console.error('加载知识库列表失败:', error);
alert('加载知识库列表失败');
console.error('删除知识库失败:', error);
alert('删除知识库失败');
}
}
// 加载文件列表
async function loadFileList(kbId) {
if (!kbId) return;
// 删除文件
async function deleteFile(fileId) {
if (!confirm('确定要删除这个文件吗?')) return;
try {
const response = await axios.get(`${API_BASE_URL}/kb_files?kb_id=${kbId}`);
const fileList = response.data;
const tbody = document.getElementById('file-table-body');
tbody.innerHTML = '';
fileList.forEach(file => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${file.id}</td>
<td>${file.file_name}</td>
<td>${formatFileSize(file.file_size)}</td>
<td>${file.file_type}</td>
<td>${getFileStatusText(file.state)}</td>
<td>${new Date(file.create_time).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-outline-danger delete-file" data-id="${file.id}">删除</button>
</td>
`;
tbody.appendChild(row);
});
// 添加删除按钮的事件
document.querySelectorAll('.delete-file').forEach(btn => {
btn.addEventListener('click', function() {
deleteFile(this.getAttribute('data-id'));
});
});
await axios.delete(`${API_BASE_URL}/kb_files/${fileId}`);
alert('删除成功');
const kbId = document.getElementById('kb-selector').value;
if (kbId) loadFileList(kbId);
} catch (error) {
console.error('加载文件列表失败:', error);
alert('加载文件列表失败');
console.error('删除文件失败:', error);
alert('删除文件失败');
}
}
// 辅助函数:格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
// 在initApp()函数前添加以下代码
function loadKbList() {
axios.get(`${API_BASE_URL}/kb`)
.then(response => {
const container = document.getElementById('kb-table-body');
container.innerHTML = '';
response.data.forEach(kb => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${kb.id}</td>
<td>${kb.kb_name}</td>
<td>${kb.short_name || ''}</td>
<td>${new Date(kb.create_time).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-primary edit-kb" data-id="${kb.id}">编辑</button>
<button class="btn btn-sm btn-danger delete-kb" data-id="${kb.id}">删除</button>
</td>
`;
container.appendChild(row);
});
bindKbActions();
})
.catch(error => {
console.error('加载知识库列表失败:', error);
alert('加载知识库列表失败: ' + error.message);
});
}
// 辅助函数:获取文件状态文本
function getFileStatusText(state) {
switch (state) {
case 0: return '待处理';
case 1: return '已处理';
case 2: return '处理失败';
default: return '未知状态';
}
function loadFileList(kbId) {
// 实现文件列表加载逻辑
}
// 显示知识库模态框
function showKbModal(kbId = null) {
// 实现知识库添加/编辑模态框逻辑
function bindKbActions() {
// 绑定编辑按钮
document.querySelectorAll('.edit-kb').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const kbId = this.dataset.id;
showKbModal(kbId);
});
});
// 绑定删除按钮
document.querySelectorAll('.delete-kb').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const kbId = this.dataset.id;
if(confirm('确定要删除这个知识库吗?')) {
deleteKb(kbId);
}
});
});
}
// 确保initApp函数已定义
function initApp() {
loadKbList();
// 重新绑定所有事件监听器
$('#add-kb-btn').click(function(e) {
e.preventDefault();
showKbModal();
});
$('#kb-selector').change(function() {
loadFileList($(this).val());
});
console.log('应用初始化完成');
}
// 使用jQuery的文档就绪函数
$(function() {
try {
initApp();
} catch (e) {
console.error('初始化失败:', e);
alert('系统初始化失败: ' + e.message);
}
});
function showKbModal(kbId) {
// 确保kbId是数字或字符串
const id = typeof kbId === 'object' ? kbId.id : kbId;
if (!id) {
// 新建知识库模式
$('#kb-name').val('');
$('#kb-desc').val('');
$('#kb-modal').modal('show');
$('#kb-modal-title').text('新建知识库');
$('#kb-submit-btn').text('创建');
currentEditKbId = null;
return;
}
$.ajax({
url: API_BASE_URL + '/kb/' + id,
type: 'GET',
success: function(response) {
if (response) {
$('#kb-name').val(response.kb_name);
$('#kb-desc').val(response.short_name);
$('#kb-modal').modal('show');
$('#kb-modal-title').text('编辑知识库');
$('#kb-submit-btn').text('更新');
currentEditKbId = id;
}
},
error: function(xhr) {
console.error('加载知识库详情失败:', xhr.responseText);
alert('加载知识库详情失败: ' + (xhr.responseJSON?.message || xhr.statusText));
}
});
}
// 显示文件上传模态框
@ -168,4 +347,115 @@ async function deleteFile(fileId) {
console.error('删除文件失败:', error);
alert('删除文件失败');
}
}
// 在initApp()函数前添加以下代码
function loadKbList() {
axios.get(`${API_BASE_URL}/kb`)
.then(response => {
const container = document.getElementById('kb-table-body');
container.innerHTML = '';
response.data.forEach(kb => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${kb.id}</td>
<td>${kb.kb_name}</td>
<td>${kb.short_name || ''}</td>
<td>${new Date(kb.create_time).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-primary edit-kb" data-id="${kb.id}">编辑</button>
<button class="btn btn-sm btn-danger delete-kb" data-id="${kb.id}">删除</button>
</td>
`;
container.appendChild(row);
});
bindKbActions();
})
.catch(error => {
console.error('加载知识库列表失败:', error);
alert('加载知识库列表失败: ' + error.message);
});
}
function loadFileList(kbId) {
// 实现文件列表加载逻辑
}
function bindKbActions() {
// 绑定编辑按钮
document.querySelectorAll('.edit-kb').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const kbId = this.dataset.id;
showKbModal(kbId);
});
});
// 绑定删除按钮
document.querySelectorAll('.delete-kb').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const kbId = this.dataset.id;
if(confirm('确定要删除这个知识库吗?')) {
deleteKb(kbId);
}
});
});
}
// 确保initApp函数已定义
function initApp() {
loadKbList();
// 重新绑定所有事件监听器
$('#add-kb-btn').click(function(e) {
e.preventDefault();
showKbModal();
});
$('#kb-selector').change(function() {
loadFileList($(this).val());
});
console.log('应用初始化完成');
}
// 使用jQuery的文档就绪函数
$(function() {
try {
initApp();
} catch (e) {
console.error('初始化失败:', e);
alert('系统初始化失败: ' + e.message);
}
});
function loadKbDetail(kbId) {
$.ajax({
url: API_BASE_URL + '/kb/' + kbId,
type: 'GET',
success: function(response) {
if (response) {
// 填充知识库详情到表单
$('#kb-name').val(response.kb_name);
$('#kb-desc').val(response.short_name);
// 显示编辑模态框
$('#kb-modal').modal('show');
$('#kb-modal-title').text('编辑知识库');
$('#kb-submit-btn').text('更新');
// 存储当前编辑的知识库ID
currentEditKbId = kbId;
} else {
alert('获取知识库详情失败:数据为空');
}
},
error: function(xhr) {
console.error('加载知识库详情失败:', xhr.responseText);
alert('加载知识库详情失败: ' + xhr.statusText);
}
});
}
Loading…
Cancel
Save