2468 lines
82 KiB
PHP
2468 lines
82 KiB
PHP
|
|
<?php
|
|||
|
|
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
|||
|
|
/**
|
|||
|
|
* 知识插件后台管理面板
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 获取配置
|
|||
|
|
$options = Typecho_Widget::widget('Widget_Options');
|
|||
|
|
$config = $options->plugin('Memo');
|
|||
|
|
|
|||
|
|
// 引入Action类
|
|||
|
|
require_once __DIR__ . '/Action.php';
|
|||
|
|
$action = new Memo_Action();
|
|||
|
|
|
|||
|
|
// 获取搜索和筛选参数
|
|||
|
|
$search = isset($_GET['search']) ? trim($_GET['search']) : '';
|
|||
|
|
$category = isset($_GET['category']) ? trim($_GET['category']) : '';
|
|||
|
|
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
|
|||
|
|
$perPage = isset($config->perPage) ? intval($config->perPage) : 10;
|
|||
|
|
|
|||
|
|
// 获取所有分类
|
|||
|
|
$allCategories = $action->getAllCategories();
|
|||
|
|
$categoryCounts = $action->getCategoryCounts();
|
|||
|
|
|
|||
|
|
// 获取统计信息
|
|||
|
|
$statistics = $action->getStatistics();
|
|||
|
|
|
|||
|
|
// 获取默认分类设置
|
|||
|
|
$defaultCategories = isset($config->defaultCategories) ? explode("\n", $config->defaultCategories) : array('默认');
|
|||
|
|
$defaultCategories = array_map('trim', $defaultCategories);
|
|||
|
|
$defaultCategories = array_filter($defaultCategories);
|
|||
|
|
|
|||
|
|
// 处理表单提交
|
|||
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
|
|
// 处理新增
|
|||
|
|
if (!empty($_POST['content'])) {
|
|||
|
|
try {
|
|||
|
|
$action->addMemo(array(
|
|||
|
|
'content' => $_POST['content'],
|
|||
|
|
'category' => isset($_POST['category']) ? $_POST['category'] : '默认',
|
|||
|
|
'event_date' => isset($_POST['event_date']) && !empty($_POST['event_date']) ? $_POST['event_date'] : null,
|
|||
|
|
'post_cids' => isset($_POST['post_cids']) ? $_POST['post_cids'] : '',
|
|||
|
|
'original_url' => isset($_POST['original_url']) ? $_POST['original_url'] : '' // 新增:原文链接
|
|||
|
|
));
|
|||
|
|
|
|||
|
|
$successMsg = '知识添加成功!';
|
|||
|
|
// 重定向到当前页面(保持筛选状态)
|
|||
|
|
$redirectUrl = $_SERVER['PHP_SELF'] . '?panel=Memo/manage-panel.php&page=' . $page;
|
|||
|
|
if ($search) $redirectUrl .= '&search=' . urlencode($search);
|
|||
|
|
if ($category) $redirectUrl .= '&category=' . urlencode($category);
|
|||
|
|
$redirectUrl .= '#memo-list-anchor';
|
|||
|
|
header('Location: ' . $redirectUrl);
|
|||
|
|
exit;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
$errorMsg = '添加失败: ' . htmlspecialchars($e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理批量删除 - 修复:独立的批量删除处理
|
|||
|
|
if (isset($_POST['bulk_delete']) && !empty($_POST['delete_ids']) && is_array($_POST['delete_ids'])) {
|
|||
|
|
$action->deleteMemos($_POST['delete_ids']);
|
|||
|
|
$successMsg = '删除成功!';
|
|||
|
|
// 重定向到当前页面(保持筛选状态)
|
|||
|
|
$redirectUrl = $_SERVER['PHP_SELF'] . '?panel=Memo/manage-panel.php&page=' . $page;
|
|||
|
|
if ($search) $redirectUrl .= '&search=' . urlencode($search);
|
|||
|
|
if ($category) $redirectUrl .= '&category=' . urlencode($category);
|
|||
|
|
$redirectUrl .= '#memo-list-anchor';
|
|||
|
|
header('Location: ' . $redirectUrl);
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理批量导出TXT - 修复:正确处理导出数据
|
|||
|
|
if (isset($_POST['export_selected_txt']) && !empty($_POST['export_ids'])) {
|
|||
|
|
try {
|
|||
|
|
// 将逗号分隔的字符串转换为数组
|
|||
|
|
$exportIds = explode(',', $_POST['export_ids']);
|
|||
|
|
$exportIds = array_map('intval', $exportIds);
|
|||
|
|
$exportIds = array_filter($exportIds);
|
|||
|
|
|
|||
|
|
if (empty($exportIds)) {
|
|||
|
|
throw new Exception('没有选择有效的记录');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$exportContent = $action->exportSelectedData($exportIds, 'txt');
|
|||
|
|
$filename = 'memo_selected_' . date('Ymd_His') . '.txt';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/plain');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
$errorMsg = '导出失败: ' . htmlspecialchars($e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理批量导出MD - 修复:正确处理导出数据
|
|||
|
|
if (isset($_POST['export_selected_md']) && !empty($_POST['export_ids'])) {
|
|||
|
|
try {
|
|||
|
|
// 将逗号分隔的字符串转换为数组
|
|||
|
|
$exportIds = explode(',', $_POST['export_ids']);
|
|||
|
|
$exportIds = array_map('intval', $exportIds);
|
|||
|
|
$exportIds = array_filter($exportIds);
|
|||
|
|
|
|||
|
|
if (empty($exportIds)) {
|
|||
|
|
throw new Exception('没有选择有效的记录');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$exportContent = $action->exportSelectedData($exportIds, 'md');
|
|||
|
|
$filename = 'memo_selected_' . date('Ymd_His') . '.md';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/markdown');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
$errorMsg = '导出失败: ' . htmlspecialchars($e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理单个导出
|
|||
|
|
if (isset($_POST['export_single_txt']) && !empty($_POST['single_id'])) {
|
|||
|
|
try {
|
|||
|
|
$exportContent = $action->exportSelectedData(array(intval($_POST['single_id'])), 'txt');
|
|||
|
|
$filename = 'memo_' . $_POST['single_id'] . '_' . date('Ymd_His') . '.txt';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/plain');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
$errorMsg = '导出失败: ' . htmlspecialchars($e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (isset($_POST['export_single_md']) && !empty($_POST['single_id'])) {
|
|||
|
|
try {
|
|||
|
|
$exportContent = $action->exportSelectedData(array(intval($_POST['single_id'])), 'md');
|
|||
|
|
$filename = 'memo_' . $_POST['single_id'] . '_' . date('Ymd_His') . '.md';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/markdown');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
$errorMsg = '导出失败: ' . htmlspecialchars($e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理编辑
|
|||
|
|
if (!empty($_POST['edit_id'])) {
|
|||
|
|
$action->updateMemo(array(
|
|||
|
|
'edit_id' => $_POST['edit_id'],
|
|||
|
|
'edit_content' => $_POST['edit_content'],
|
|||
|
|
'edit_category' => isset($_POST['edit_category']) ? $_POST['edit_category'] : '默认',
|
|||
|
|
'edit_event_date' => isset($_POST['edit_event_date']) && !empty($_POST['edit_event_date']) ? $_POST['edit_event_date'] : null,
|
|||
|
|
'edit_post_cids' => isset($_POST['edit_post_cids']) ? $_POST['edit_post_cids'] : '',
|
|||
|
|
'edit_original_url' => isset($_POST['edit_original_url']) ? $_POST['edit_original_url'] : ''
|
|||
|
|
));
|
|||
|
|
$successMsg = '知识更新成功!';
|
|||
|
|
// 重定向到当前页面(保持筛选状态)
|
|||
|
|
$redirectUrl = $_SERVER['PHP_SELF'] . '?panel=Memo/manage-panel.php&page=' . $page;
|
|||
|
|
if ($search) $redirectUrl .= '&search=' . urlencode($search);
|
|||
|
|
if ($category) $redirectUrl .= '&category=' . urlencode($category);
|
|||
|
|
$redirectUrl .= '#memo-list-anchor';
|
|||
|
|
header('Location: ' . $redirectUrl);
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理全量导出
|
|||
|
|
if (isset($_POST['export'])) {
|
|||
|
|
$exportContent = $action->exportData();
|
|||
|
|
$filename = 'memo_' . date('Ymd_His') . '.txt';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/plain');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理全量导出为MD
|
|||
|
|
if (isset($_POST['export_md'])) {
|
|||
|
|
$exportContent = $action->exportMdData();
|
|||
|
|
$filename = 'memo_' . date('Ymd_His') . '.md';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/markdown');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理导入
|
|||
|
|
if (isset($_POST['import']) && !empty($_FILES['import_file']['tmp_name'])) {
|
|||
|
|
$fileContent = file_get_contents($_FILES['import_file']['tmp_name']);
|
|||
|
|
$fileExtension = strtolower(pathinfo($_FILES['import_file']['name'], PATHINFO_EXTENSION));
|
|||
|
|
|
|||
|
|
if ($fileExtension == 'sql') {
|
|||
|
|
// 导入SQL文件
|
|||
|
|
$importCategory = isset($_POST['import_category']) ? $_POST['import_category'] : '文章导入';
|
|||
|
|
$result = $action->importSqlData($fileContent, $importCategory);
|
|||
|
|
} else {
|
|||
|
|
// 导入文本文件
|
|||
|
|
$result = $action->importData($fileContent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($result['imported'] > 0) {
|
|||
|
|
$successMsg = '导入成功!成功导入 ' . $result['imported'] . ' 条记录';
|
|||
|
|
if ($result['failed'] > 0) {
|
|||
|
|
$successMsg .= ',失败 ' . $result['failed'] . ' 条记录';
|
|||
|
|
|
|||
|
|
if (!empty($result['fail_reasons'])) {
|
|||
|
|
$errorDetails = '<strong>失败原因:</strong><br>';
|
|||
|
|
$displayCount = min(10, count($result['fail_reasons']));
|
|||
|
|
for ($i = 0; $i < $displayCount; $i++) {
|
|||
|
|
$errorDetails .= htmlspecialchars($result['fail_reasons'][$i]) . '<br>';
|
|||
|
|
}
|
|||
|
|
if (count($result['fail_reasons']) > 10) {
|
|||
|
|
$errorDetails .= '...还有 ' . (count($result['fail_reasons']) - 10) . ' 条失败记录';
|
|||
|
|
}
|
|||
|
|
$errorMsg = $errorDetails;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$errorMsg = '导入失败!未找到可导入的记录或格式不正确';
|
|||
|
|
if (!empty($result['fail_reasons'])) {
|
|||
|
|
$errorMsg .= '<br><strong>失败原因:</strong><br>';
|
|||
|
|
$displayCount = min(10, count($result['fail_reasons']));
|
|||
|
|
for ($i = 0; $i < $displayCount; $i++) {
|
|||
|
|
$errorMsg .= htmlspecialchars($result['fail_reasons'][$i]) . '<br>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取知识数据
|
|||
|
|
$memos = $action->getMemos($page, $perPage, $search, $category);
|
|||
|
|
$total = $action->getTotalCount($search, $category);
|
|||
|
|
$totalPages = ceil($total / $perPage);
|
|||
|
|
|
|||
|
|
// 为每条记录获取关联的文章信息
|
|||
|
|
foreach ($memos as &$memo) {
|
|||
|
|
$memo['post_info'] = array();
|
|||
|
|
if (!empty($memo['post_cids'])) {
|
|||
|
|
$cids = array_filter(array_map('trim', explode(',', $memo['post_cids'])));
|
|||
|
|
foreach ($cids as $cid) {
|
|||
|
|
if (is_numeric($cid)) {
|
|||
|
|
$post = $action->getPostByCid($cid);
|
|||
|
|
if ($post) {
|
|||
|
|
$memo['post_info'][$cid] = $post;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
unset($memo);
|
|||
|
|
|
|||
|
|
// 辅助函数:将URL转换为链接(精确匹配)
|
|||
|
|
function convertUrlsToLinks($text) {
|
|||
|
|
if (empty($text)) {
|
|||
|
|
return $text;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 先进行HTML实体编码
|
|||
|
|
$encodedText = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
|
|||
|
|
|
|||
|
|
// 精确匹配URL的正则表达式
|
|||
|
|
// 只匹配以http://或https://开头的完整URL
|
|||
|
|
$urlPattern = '/(https?:\/\/[a-zA-Z0-9][-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/';
|
|||
|
|
|
|||
|
|
// 使用preg_replace_callback进行精确处理
|
|||
|
|
return preg_replace_callback($urlPattern, function($matches) {
|
|||
|
|
$url = $matches[1];
|
|||
|
|
|
|||
|
|
// 清理URL末尾可能错误的标点符号
|
|||
|
|
$punctuation = array('.', ',', ';', ':', '!', '?', ')', ']', '}');
|
|||
|
|
$lastChar = substr($url, -1);
|
|||
|
|
if (in_array($lastChar, $punctuation)) {
|
|||
|
|
$url = substr($url, 0, -1);
|
|||
|
|
$suffix = $lastChar;
|
|||
|
|
} else {
|
|||
|
|
$suffix = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保URL格式正确
|
|||
|
|
if (!preg_match('/^https?:\/\//', $url)) {
|
|||
|
|
return $matches[0]; // 如果不是合法URL,返回原文本
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建链接
|
|||
|
|
return '<a href="' . htmlspecialchars($url) . '" class="url-link" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($url) . '</a>' . $suffix;
|
|||
|
|
}, $encodedText);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
<?php include 'header.php'; ?>
|
|||
|
|
<?php include 'menu.php'; ?>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
|
|||
|
|
/* 分类横向列表样式 - 修改为自动换行 */
|
|||
|
|
.categories-horizontal-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding-bottom: 5px;
|
|||
|
|
align-items: center;
|
|||
|
|
max-height: 100px; /* 限制最大高度,超出可滚动 */
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-tag {
|
|||
|
|
display: inline-block;
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
background: #e8f5e8;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
border: 1px solid #c8e6c9;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
text-decoration: none;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
cursor: pointer;
|
|||
|
|
flex-shrink: 0; /* 防止标签被压缩 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-tag:hover {
|
|||
|
|
background: #d5ecd5;
|
|||
|
|
border-color: #a8d5a9;
|
|||
|
|
text-decoration: none;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 确保统计卡片内容不换行 */
|
|||
|
|
.stats-card h3 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
padding-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-card .categories-horizontal-list {
|
|||
|
|
margin-top: 8px;
|
|||
|
|
min-height: 32px;
|
|||
|
|
}
|
|||
|
|
/* 基础样式 */
|
|||
|
|
.memo-panel {
|
|||
|
|
padding: 40px 0;
|
|||
|
|
margin-left: 150px;
|
|||
|
|
margin-top:50px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.panel-container {
|
|||
|
|
max-width: 1200px;
|
|||
|
|
margin: 0 auto;
|
|||
|
|
padding: 0 30px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 浮动消息 */
|
|||
|
|
.floating-message {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 20px;
|
|||
|
|
right: 20px;
|
|||
|
|
z-index: 9999;
|
|||
|
|
max-width: 350px;
|
|||
|
|
opacity: 0;
|
|||
|
|
transform: translateY(-20px);
|
|||
|
|
transition: opacity 0.3s, transform 0.3s;
|
|||
|
|
padding: 14px 20px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message.show {
|
|||
|
|
opacity: 1;
|
|||
|
|
transform: translateY(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message.success {
|
|||
|
|
color: #0f5132;
|
|||
|
|
background-color: #d1e7dd;
|
|||
|
|
border-color: #badbcc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message.error {
|
|||
|
|
color: #842029;
|
|||
|
|
background-color: #f8d7da;
|
|||
|
|
border-color: #f5c2c7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 统计卡片样式 */
|
|||
|
|
.stats-section {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|||
|
|
gap: 20px;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-card {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 20px;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-card h3 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
padding-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-number {
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #007bff;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-label {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-date {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-category-item {
|
|||
|
|
padding: 10px 0;
|
|||
|
|
border-bottom: 1px solid #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-category-item:last-child {
|
|||
|
|
border-bottom: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-category-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-category-name {
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #333;
|
|||
|
|
text-decoration: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-category-name:hover {
|
|||
|
|
color: #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-category-count {
|
|||
|
|
background: #e8f5e8;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
border-radius: 10px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-empty {
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 20px;
|
|||
|
|
color: #999;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 批量操作表单修复 */
|
|||
|
|
.bulk-form {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bulk-actions .bulk-actions-buttons button {
|
|||
|
|
margin: 0 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 导入导出 */
|
|||
|
|
.import-export-section {
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-export-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: 1fr 1fr;
|
|||
|
|
gap: 22px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-box, .export-box {
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 22px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-box h4, .export-box h4 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
color: #495057;
|
|||
|
|
font-size: 15px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.export-buttons {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 10px;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.format-example {
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
padding: 16px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border-left: 3px solid #007bff;
|
|||
|
|
margin-top: 18px;
|
|||
|
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.format-example pre {
|
|||
|
|
margin: 5px 0px;
|
|||
|
|
white-space: pre-wrap;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#post_cids, #original_url{height:36px;border-radius:4px!important;}
|
|||
|
|
|
|||
|
|
/* 发布表单 */
|
|||
|
|
.publish-form {
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 25px;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.publish-form h2 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
padding-bottom: 16px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-row {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 20px;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group {
|
|||
|
|
flex: 1;
|
|||
|
|
min-width: 200px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group label {
|
|||
|
|
display: block;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
margin-top:8px ;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #555;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
margin-top:5px;
|
|||
|
|
color: #333;
|
|||
|
|
background-color: #fff;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
textarea.form-control {
|
|||
|
|
resize: vertical;
|
|||
|
|
min-height: 100px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修复:日期输入框宽度问题 */
|
|||
|
|
input[type="date"].form-control {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修复1:分类下拉选择框 - 确保文字完整显示 */
|
|||
|
|
select.form-control {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
background-color: #fff;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
max-width: 100%;
|
|||
|
|
height: auto; /* 允许高度自动调整 */
|
|||
|
|
min-height: 42px; /* 设置最小高度 */
|
|||
|
|
line-height: 1.5; /* 设置行高 */
|
|||
|
|
overflow: visible !important; /* 强制显示完整内容 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修复1:分类下拉选项 - 确保文字完整显示 */
|
|||
|
|
select.form-control option {
|
|||
|
|
padding: 10px 12px; /* 增加内边距 */
|
|||
|
|
font-size: 14px;
|
|||
|
|
line-height: 1.5; /* 设置行高 */
|
|||
|
|
height: auto; /* 允许高度自动调整 */
|
|||
|
|
white-space: normal; /* 允许文字换行 */
|
|||
|
|
overflow: visible; /* 允许内容溢出可见 */
|
|||
|
|
text-overflow: clip; /* 不截断文字 */
|
|||
|
|
max-width: 100%;
|
|||
|
|
word-wrap: break-word; /* 允许单词换行 */
|
|||
|
|
word-break: break-word; /* 允许单词内断行 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-actions {
|
|||
|
|
text-align: center;
|
|||
|
|
margin-top: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.description {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #6c757d;
|
|||
|
|
margin-top: 6px;
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 按钮样式 */
|
|||
|
|
.btn {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 10px 18px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
text-align: center;
|
|||
|
|
cursor: pointer;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
height: 40px;
|
|||
|
|
min-width: 80px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #007bff;
|
|||
|
|
border-color: #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary:hover {
|
|||
|
|
background-color: #0069d9;
|
|||
|
|
border-color: #0062cc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-secondary {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #6c757d;
|
|||
|
|
border-color: #6c757d;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-secondary:hover {
|
|||
|
|
background-color: #5a6268;
|
|||
|
|
border-color: #545b62;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-danger {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #dc3545;
|
|||
|
|
border-color: #dc3545;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-danger:hover {
|
|||
|
|
background-color: #c82333;
|
|||
|
|
border-color: #bd2130;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-success {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #28a745;
|
|||
|
|
border-color: #28a745;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-success:hover {
|
|||
|
|
background-color: #218838;
|
|||
|
|
border-color: #1e7e34;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-info {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #17a2b8;
|
|||
|
|
border-color: #17a2b8;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-info:hover {
|
|||
|
|
background-color: #138496;
|
|||
|
|
border-color: #117a8b;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-warning {
|
|||
|
|
color: #212529;
|
|||
|
|
background-color: #ffc107;
|
|||
|
|
border-color: #ffc107;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-warning:hover {
|
|||
|
|
background-color: #e0a800;
|
|||
|
|
border-color: #d39e00;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-sm {
|
|||
|
|
padding: 6px 12px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
height: 36px;
|
|||
|
|
min-width: 60px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 知识列表区域 */
|
|||
|
|
.memo-list-section {
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 0;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-list-section h2 {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 20px 25px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 搜索和筛选工具栏 */
|
|||
|
|
.search-filter-toolbar {
|
|||
|
|
padding: 15px 25px;
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 15px;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-box {
|
|||
|
|
flex: 1;
|
|||
|
|
min-width: 250px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-box form {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-input {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px!important;
|
|||
|
|
overflow: hidden;
|
|||
|
|
font-size: 14px;
|
|||
|
|
min-width: 200px;
|
|||
|
|
height: 36px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-box {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
align-items: center;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-filter {
|
|||
|
|
min-width: 150px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修复1:分类筛选下拉框 - 确保文字完整显示 */
|
|||
|
|
.category-select {
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
background: white;
|
|||
|
|
min-width: 150px;
|
|||
|
|
max-width: 200px;
|
|||
|
|
height: auto;
|
|||
|
|
min-height: 36px;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
overflow: visible !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-select option {
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
height: auto;
|
|||
|
|
white-space: normal;
|
|||
|
|
overflow: visible;
|
|||
|
|
text-overflow: clip;
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
word-break: break-word;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-info {
|
|||
|
|
padding: 8px 15px;
|
|||
|
|
background: #e7f3ff;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
color: #0066cc;
|
|||
|
|
font-size: 13px;
|
|||
|
|
flex-grow: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-info strong {
|
|||
|
|
color: #0056b3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.clear-filter {
|
|||
|
|
color: #666;
|
|||
|
|
text-decoration: none;
|
|||
|
|
font-size: 13px;
|
|||
|
|
margin-left: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.clear-filter:hover {
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 表格样式 */
|
|||
|
|
.data-table {
|
|||
|
|
width: 100%;
|
|||
|
|
border-collapse: separate;
|
|||
|
|
border-spacing: 0;
|
|||
|
|
margin: 0;
|
|||
|
|
font-size: 14px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
table-layout: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th,
|
|||
|
|
.data-table td {
|
|||
|
|
padding: 14px 12px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
text-align: left;
|
|||
|
|
vertical-align: middle;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th {
|
|||
|
|
background-color: #fafbfc;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
border-bottom: 2px solid #e0e0e0;
|
|||
|
|
font-size: 14px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table tbody tr:hover {
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 列宽设置 - 修复 */
|
|||
|
|
.data-table th:nth-child(1),
|
|||
|
|
.data-table td:nth-child(1) {
|
|||
|
|
width: 30px;
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 14px 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(2),
|
|||
|
|
.data-table td:nth-child(2) {
|
|||
|
|
width: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #666;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 内容列 - 问题3:超出显示查看更多 */
|
|||
|
|
.data-table th:nth-child(3),
|
|||
|
|
.data-table td:nth-child(3) {
|
|||
|
|
min-width: 350px;
|
|||
|
|
max-width: 400px; /* 限制最大宽度 */
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
overflow-wrap: break-word;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(4),
|
|||
|
|
.data-table td:nth-child(4) {
|
|||
|
|
width: 50px;
|
|||
|
|
text-align: center;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(5),
|
|||
|
|
.data-table td:nth-child(5) {
|
|||
|
|
width: 50px;
|
|||
|
|
text-align: center;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(6),
|
|||
|
|
.data-table td:nth-child(6) {
|
|||
|
|
width: 80px;
|
|||
|
|
text-align: center;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(7),
|
|||
|
|
.data-table td:nth-child(7) {
|
|||
|
|
width: 120px;
|
|||
|
|
min-width: 100px;
|
|||
|
|
max-width: 150px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(8),
|
|||
|
|
.data-table td:nth-child(8) {
|
|||
|
|
width: 200px; /* 增加宽度以容纳更多按钮 */
|
|||
|
|
text-align: center;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
padding: 14px 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 分类徽章 - 修改为可点击 */
|
|||
|
|
.category-badge {
|
|||
|
|
display: inline-block;
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
background: #e8f5e8;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
border: 1px solid #c8e6c9;
|
|||
|
|
max-width: 120px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
cursor: pointer;
|
|||
|
|
text-decoration: none;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-badge:hover {
|
|||
|
|
background: #d5ecd5;
|
|||
|
|
border-color: #a8d5a9;
|
|||
|
|
text-decoration: none;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 日期徽章 */
|
|||
|
|
.date-badge {
|
|||
|
|
display: inline-block;
|
|||
|
|
background-color: #f0f7ff;
|
|||
|
|
color: #0066cc;
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 创建时间 */
|
|||
|
|
.created-time {
|
|||
|
|
color: #666;
|
|||
|
|
font-size: 12px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* CID徽章 - 修改为可点击链接 */
|
|||
|
|
.cid-badge {
|
|||
|
|
display: inline-block;
|
|||
|
|
background-color: #fff3cd;
|
|||
|
|
color: #856404;
|
|||
|
|
padding: 3px 8px;
|
|||
|
|
margin: 2px;
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-size: 11px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|||
|
|
border: 1px solid #ffeaa7;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
cursor: pointer;
|
|||
|
|
text-decoration: none;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cid-badge:hover {
|
|||
|
|
background-color: #ffeaa7;
|
|||
|
|
border-color: #ffdd59;
|
|||
|
|
text-decoration: none;
|
|||
|
|
color: #856404;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cid-container {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 3px;
|
|||
|
|
max-height: 60px;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 批量操作 */
|
|||
|
|
.bulk-actions {
|
|||
|
|
padding: 15px;
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bulk-actions-footer {
|
|||
|
|
border-top: none;
|
|||
|
|
border-bottom: 2px solid #e0e0e0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.select-all-area {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.selected-info {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 新增:批量操作按钮组 */
|
|||
|
|
.bulk-actions-buttons {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 分页样式 */
|
|||
|
|
.pagination {
|
|||
|
|
margin-top: 25px;
|
|||
|
|
margin-bottom: 25px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination ul {
|
|||
|
|
list-style: none;
|
|||
|
|
padding: 0;
|
|||
|
|
margin: 0;
|
|||
|
|
display: inline-flex;
|
|||
|
|
gap: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination li {
|
|||
|
|
display: inline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination a, .pagination span {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
color: #495057;
|
|||
|
|
text-decoration: none;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
min-width: 36px;
|
|||
|
|
height: 36px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination a:hover {
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
border-color: #dee2e6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination .active a, .pagination .active span {
|
|||
|
|
background-color: #007bff;
|
|||
|
|
color: white;
|
|||
|
|
border-color: #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 空状态 */
|
|||
|
|
.empty-state {
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 50px 20px;
|
|||
|
|
color: #999;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-state .icon {
|
|||
|
|
font-size: 48px;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
opacity: 0.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 模态框 */
|
|||
|
|
.modal {
|
|||
|
|
display: none;
|
|||
|
|
position: fixed;
|
|||
|
|
z-index: 1000;
|
|||
|
|
left: 0;
|
|||
|
|
top: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background-color: rgba(0,0,0,0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-content {
|
|||
|
|
background-color: #fff;
|
|||
|
|
margin: 3.5% auto;
|
|||
|
|
padding: 0;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
width: 85%;
|
|||
|
|
max-width: 800px; /* 增加最大宽度用于内容查看 */
|
|||
|
|
box-shadow: 0 5px 25px rgba(0,0,0,0.15);
|
|||
|
|
position: relative;
|
|||
|
|
max-height: 87vh; /* 限制最大高度 */
|
|||
|
|
overflow-y: auto; /* 允许垂直滚动 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close {
|
|||
|
|
position: absolute;
|
|||
|
|
right: 20px;
|
|||
|
|
top: 18px;
|
|||
|
|
font-size: 22px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
color: #999;
|
|||
|
|
background: none;
|
|||
|
|
border: none;
|
|||
|
|
padding: 0;
|
|||
|
|
width: 24px;
|
|||
|
|
height: 24px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close:hover {
|
|||
|
|
color: #333;
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-header {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 20px 25px;
|
|||
|
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
|
background-color: #fafbfc;
|
|||
|
|
border-radius: 8px 8px 0 0;
|
|||
|
|
position: sticky;
|
|||
|
|
top: 0;
|
|||
|
|
z-index: 10;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-header h3 {
|
|||
|
|
margin: 0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-body {
|
|||
|
|
padding: 25px;
|
|||
|
|
max-height: calc(85vh - 70px); /* 减去头部高度 */
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 内容查看模态框特定样式 */
|
|||
|
|
#contentModal .modal-body {
|
|||
|
|
font-size: 14px;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
color: #333;
|
|||
|
|
white-space: pre-wrap; /* 仅在弹窗中支持换行显示 */
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修复2:超链接样式 - 只对URL本身加链接 */
|
|||
|
|
.url-link {
|
|||
|
|
color: #0066cc;
|
|||
|
|
text-decoration: none;
|
|||
|
|
border-bottom: 1px dotted #0066cc;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
word-break: break-all; /* 允许URL换行 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.url-link:hover {
|
|||
|
|
color: #0056b3;
|
|||
|
|
background-color: #f8f9fa;;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 原文链接样式 */
|
|||
|
|
.original-link {
|
|||
|
|
margin-top: 15px;
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border-left: 3px solid #28a745;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.original-link-label {
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #495057;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.original-link-url {
|
|||
|
|
font-size: 13px;
|
|||
|
|
word-break: break-all;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.original-link-url a {
|
|||
|
|
color: #0066cc;
|
|||
|
|
text-decoration: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.original-link-url a:hover {
|
|||
|
|
text-decoration: underline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-content form {
|
|||
|
|
padding: 25px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 查看更多按钮 */
|
|||
|
|
.view-more-btn {
|
|||
|
|
display: inline-block;
|
|||
|
|
color: #0066cc;
|
|||
|
|
background: none;
|
|||
|
|
border: none;
|
|||
|
|
padding: 2px 6px 2px 0px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
margin-top: 5px;
|
|||
|
|
border-radius: 3px;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.view-more-btn:hover {
|
|||
|
|
background-color: #f0f7ff;
|
|||
|
|
text-decoration: underline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 内容容器 */
|
|||
|
|
.content-container {
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.content-preview {
|
|||
|
|
max-height: 80px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
position: relative;
|
|||
|
|
color: #000;
|
|||
|
|
/* 修复1:移除 white-space: pre-wrap,列表不处理换行 */
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 操作按钮组 */
|
|||
|
|
.action-buttons {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 5px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 锚点目标 */
|
|||
|
|
.anchor-target {
|
|||
|
|
scroll-margin-top: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 响应式设计 */
|
|||
|
|
@media (max-width: 992px) {
|
|||
|
|
.import-export-grid {
|
|||
|
|
grid-template-columns: 1fr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-filter-toolbar {
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: stretch;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-box {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-box {
|
|||
|
|
width: 100%;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table {
|
|||
|
|
display: block;
|
|||
|
|
overflow-x: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(8),
|
|||
|
|
.data-table td:nth-child(8) {
|
|||
|
|
width: 150px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-buttons {
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.panel-container {
|
|||
|
|
padding: 0 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-row {
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bulk-actions {
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bulk-actions-buttons {
|
|||
|
|
width: 100%;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.select-all-area {
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message {
|
|||
|
|
left: 20px;
|
|||
|
|
right: 20px;
|
|||
|
|
max-width: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-box form {
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-input {
|
|||
|
|
min-width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-filter {
|
|||
|
|
min-width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-select {
|
|||
|
|
min-width: 100%;
|
|||
|
|
max-width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(3),
|
|||
|
|
.data-table td:nth-child(3) {
|
|||
|
|
max-width: 200px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:nth-child(8),
|
|||
|
|
.data-table td:nth-child(8) {
|
|||
|
|
width: 120px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.export-buttons {
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.export-buttons button {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 480px) {
|
|||
|
|
.pagination a, .pagination span {
|
|||
|
|
min-width: 30px;
|
|||
|
|
height: 30px;
|
|||
|
|
padding: 0 8px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-buttons button {
|
|||
|
|
font-size: 11px;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
<div class="memo-panel">
|
|||
|
|
<div class="panel-container">
|
|||
|
|
|
|||
|
|
<!-- 浮动消息提示 -->
|
|||
|
|
<?php if (isset($successMsg)): ?>
|
|||
|
|
<div id="floatingMessage" class="floating-message success show">
|
|||
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|||
|
|
<div style="flex-grow: 1;">
|
|||
|
|
<strong>✓ 成功</strong>
|
|||
|
|
<div style="margin-top: 5px;"><?php echo $successMsg; ?></div>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" onclick="hideMessage()" style="background: none; border: none; color: inherit; font-size: 18px; cursor: pointer; margin-left: 10px; padding: 0 5px;">×</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if (isset($errorMsg)): ?>
|
|||
|
|
<div id="floatingMessage" class="floating-message error show">
|
|||
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|||
|
|
<div style="flex-grow: 1;">
|
|||
|
|
<strong>✗ 错误</strong>
|
|||
|
|
<div style="margin-top: 5px;"><?php echo $errorMsg; ?></div>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" onclick="hideMessage()" style="background: none; border: none; color: inherit; font-size: 18px; cursor: pointer; margin-left: 10px; padding: 0 5px;">×</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<div class="typecho-page-main" role="main">
|
|||
|
|
<!-- 统计信息 -->
|
|||
|
|
<div class="stats-section">
|
|||
|
|
<!--<h2 style="margin-bottom: 20px; color: #333; font-size: 18px; font-weight: 600;">知识统计</h2>-->
|
|||
|
|
|
|||
|
|
<div class="stats-grid" style="margin-top:10px;">
|
|||
|
|
<!-- 总知识数 -->
|
|||
|
|
<div class="stats-card">
|
|||
|
|
<h3>总知识数</h3>
|
|||
|
|
<div class="stats-number"><?php echo $statistics['total_count']; ?></div>
|
|||
|
|
<div class="stats-label">条知识记录</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 时间范围 -->
|
|||
|
|
<!-- 时间范围 -->
|
|||
|
|
<div class="stats-card">
|
|||
|
|
<h3>跨度时间</h3>
|
|||
|
|
<?php if ($statistics['oldest_date'] && $statistics['newest_date']): ?>
|
|||
|
|
<div class="stats-date">
|
|||
|
|
最早:<?php echo date('Y-m-d', strtotime($statistics['oldest_date'])); ?>
|
|||
|
|
</div>
|
|||
|
|
<div class="stats-date">
|
|||
|
|
最新:<?php echo date('Y-m-d', strtotime($statistics['newest_date'])); ?>
|
|||
|
|
</div>
|
|||
|
|
<div class="stats-label" style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #f0f0f0;">
|
|||
|
|
<!--<span style="font-size: 12px; color: #666;">
|
|||
|
|
有日期:<?php echo $statistics['dated_count']; ?> 条,
|
|||
|
|
无日期:<?php echo $statistics['undated_count']; ?> 条
|
|||
|
|
</span>-->
|
|||
|
|
</div>
|
|||
|
|
<?php elseif ($statistics['oldest_date']): ?>
|
|||
|
|
<div class="stats-date">
|
|||
|
|
最早:<?php echo date('Y-m-d', strtotime($statistics['oldest_date'])); ?>
|
|||
|
|
</div>
|
|||
|
|
<div class="stats-label" style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #f0f0f0;">
|
|||
|
|
<!--<span style="font-size: 12px; color: #666;">
|
|||
|
|
有日期:<?php echo $statistics['dated_count']; ?> 条,
|
|||
|
|
无日期:<?php echo $statistics['undated_count']; ?> 条
|
|||
|
|
</span>-->
|
|||
|
|
</div>
|
|||
|
|
<?php elseif ($statistics['newest_date']): ?>
|
|||
|
|
<div class="stats-date">
|
|||
|
|
最新:<?php echo date('Y-m-d', strtotime($statistics['newest_date'])); ?>
|
|||
|
|
</div>
|
|||
|
|
<div class="stats-label" style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #f0f0f0;">
|
|||
|
|
<span style="font-size: 12px; color: #666;">
|
|||
|
|
有日期:<?php echo $statistics['dated_count']; ?> 条,
|
|||
|
|
无日期:<?php echo $statistics['undated_count']; ?> 条
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<div class="stats-empty" style="padding: 10px 0;">
|
|||
|
|
暂无事件日期数据
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 分类分布 -->
|
|||
|
|
<!-- 分类分布 -->
|
|||
|
|
<div class="stats-card">
|
|||
|
|
<h3>分类分布</h3>
|
|||
|
|
<?php if (!empty($statistics['categories'])): ?>
|
|||
|
|
<div class="categories-horizontal-list" style="margin-top: 8px;">
|
|||
|
|
<?php foreach ($statistics['categories'] as $catName => $catCount): ?>
|
|||
|
|
<a href="?panel=Memo/manage-panel.php&category=<?php echo urlencode($catName); ?>&scroll=list"
|
|||
|
|
class="category-tag"
|
|||
|
|
title="点击查看该分类的知识">
|
|||
|
|
<?php echo htmlspecialchars($catName); ?>(<?php echo $catCount; ?>)
|
|||
|
|
</a>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<div class="stats-empty">暂无分类数据</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 导入导出功能 -->
|
|||
|
|
<div class="import-export-section">
|
|||
|
|
<div class="import-export-grid">
|
|||
|
|
<!-- 导出功能 -->
|
|||
|
|
<div class="export-box">
|
|||
|
|
<h4>导出知识</h4>
|
|||
|
|
<p style="color:#000;">导出所有知识为文件格式</p>
|
|||
|
|
<div class="export-buttons">
|
|||
|
|
<form method="post" action="" style="display: inline;">
|
|||
|
|
<button type="submit" name="export" class="btn btn-success">
|
|||
|
|
导出为 TXT 文件
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
<form method="post" action="" style="display: inline;">
|
|||
|
|
<button type="submit" name="export_md" class="btn btn-warning">
|
|||
|
|
导出为 MD 文件
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
<div class="format-example">
|
|||
|
|
<strong style="color:#000;">TXT格式示例:</strong>
|
|||
|
|
<pre>[工作] 2025.12.21 完成项目报告
|
|||
|
|
[学习] PHP编程学习笔记
|
|||
|
|
[生活] 周末购物清单</pre>
|
|||
|
|
<strong style="color:#000;">MD格式示例:</strong>
|
|||
|
|
<pre>## 工作
|
|||
|
|
- **2025-12-21** 完成项目报告
|
|||
|
|
|
|||
|
|
## 学习
|
|||
|
|
- PHP编程学习笔记
|
|||
|
|
|
|||
|
|
## 生活
|
|||
|
|
- 周末购物清单</pre>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 导入功能 -->
|
|||
|
|
<div class="import-box">
|
|||
|
|
<h4>导入知识</h4>
|
|||
|
|
<p style="color:#000;">从文件导入知识</p>
|
|||
|
|
<form method="post" action="" enctype="multipart/form-data">
|
|||
|
|
<div style="margin-bottom: 15px;">
|
|||
|
|
<label for="import_file" style="color:#000;padding-bottom:3px;">选择文件:</label>
|
|||
|
|
<input type="file" id="import_file" name="import_file" class="form-control" required accept=".txt,.text,.sql,.md">
|
|||
|
|
<p class="description">支持 .txt/.md 文本文件和 .sql 数据库文件</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="margin-bottom: 15px; display: none;" id="sqlImportOptions">
|
|||
|
|
<label for="import_category">导入分类:</label>
|
|||
|
|
<input type="text" id="import_category" name="import_category" class="form-control" value="文章导入" placeholder="设置导入的分类名称">
|
|||
|
|
<p class="description">设置SQL导入的知识分类名称</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<button type="submit" name="import" class="btn btn-info">
|
|||
|
|
导入文件
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
<div class="format-example">
|
|||
|
|
<strong style="color:#000;">支持格式:</strong>
|
|||
|
|
<pre>1. TXT文本文件格式:
|
|||
|
|
[分类] 日期 内容
|
|||
|
|
日期格式: YYYY.MM.DD
|
|||
|
|
|
|||
|
|
2. MD文件格式:
|
|||
|
|
## 分类
|
|||
|
|
- **日期** 内容
|
|||
|
|
|
|||
|
|
3. SQL文件格式:
|
|||
|
|
从Typecho文章的SQL备份文件导入
|
|||
|
|
提取文章标题和内容作为知识</pre>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 发布新知识 -->
|
|||
|
|
<div class="publish-form">
|
|||
|
|
<h2>发布新知识</h2>
|
|||
|
|
<form method="post" action="" id="publishForm">
|
|||
|
|
<div class="form-row">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="category">知识分类 *</label>
|
|||
|
|
<select id="category" name="category" class="form-control" required>
|
|||
|
|
<option value="">请选择分类</option>
|
|||
|
|
<?php foreach ($defaultCategories as $cat): ?>
|
|||
|
|
<option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
<?php foreach ($allCategories as $cat): ?>
|
|||
|
|
<?php if (!in_array($cat, $defaultCategories)): ?>
|
|||
|
|
<option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="event_date">发掘日期(可选)</label>
|
|||
|
|
<input type="date" id="event_date" name="event_date" class="form-control"
|
|||
|
|
value="<?php echo date('Y-m-d'); ?>">
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="content">知识内容 *</label>
|
|||
|
|
<textarea id="content" name="content" class="form-control" rows="4" required
|
|||
|
|
placeholder="请输入知识内容(支持网址,会自动转换为超链接)"></textarea>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-row">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="post_cids">关联文章CID(可选)</label>
|
|||
|
|
<input type="text" id="post_cids" name="post_cids" class="form-control"
|
|||
|
|
placeholder="输入文章CID,多个用逗号分隔,如:123,456">
|
|||
|
|
<span class="description">可关联多个文章,输入文章CID,用逗号分隔</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="original_url">原文链接(可选)</label>
|
|||
|
|
<input type="text" id="original_url" name="original_url" class="form-control"
|
|||
|
|
placeholder="输入原文链接,如:https://www.shitoucuo.com/tandian_10.html">
|
|||
|
|
<span class="description">可填写原文链接,查看完整内容时会显示</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-actions">
|
|||
|
|
<button type="reset" class="btn btn-secondary" style="margin-right: 10px;">重置</button>
|
|||
|
|
<button type="submit" class="btn btn-primary">发布</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 锚点目标 -->
|
|||
|
|
<div id="memo-list-anchor" class="anchor-target"></div>
|
|||
|
|
|
|||
|
|
<!-- 知识列表区域 -->
|
|||
|
|
<div class="memo-list-section">
|
|||
|
|
<h2>知识列表</h2>
|
|||
|
|
|
|||
|
|
<!-- 搜索和筛选工具栏 -->
|
|||
|
|
<div class="search-filter-toolbar">
|
|||
|
|
<!-- 搜索框 -->
|
|||
|
|
<div class="search-box">
|
|||
|
|
<form method="get" action="">
|
|||
|
|
<input type="hidden" name="panel" value="Memo/manage-panel.php">
|
|||
|
|
<input type="hidden" name="category" value="<?php echo htmlspecialchars($category); ?>">
|
|||
|
|
<input type="hidden" name="scroll" value="list">
|
|||
|
|
<input type="text" name="search" class="search-input"
|
|||
|
|
placeholder="搜索知识内容..."
|
|||
|
|
value="<?php echo htmlspecialchars($search); ?>">
|
|||
|
|
<button type="submit" class="btn btn-primary btn-sm">搜索</button>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 分类筛选 -->
|
|||
|
|
<div class="filter-box">
|
|||
|
|
<div class="category-filter">
|
|||
|
|
<select name="category" class="category-select" onchange="if(this.value) window.location.href='?panel=Memo/manage-panel.php&category='+encodeURIComponent(this.value)+'<?php echo $search ? '&search=' . urlencode($search) : ''; ?>&scroll=list'; else window.location.href='?panel=Memo/manage-panel.php<?php echo $search ? '&search=' . urlencode($search) : ''; ?>&scroll=list';">
|
|||
|
|
<option value="">全部分类</option>
|
|||
|
|
<?php foreach ($allCategories as $cat): ?>
|
|||
|
|
<option value="<?php echo htmlspecialchars($cat); ?>"
|
|||
|
|
<?php echo $category == $cat ? 'selected' : ''; ?>>
|
|||
|
|
<?php echo htmlspecialchars($cat); ?>
|
|||
|
|
<?php if (isset($categoryCounts[$cat])): ?>
|
|||
|
|
(<?php echo $categoryCounts[$cat]; ?>)
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</option>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<?php if ($search || $category): ?>
|
|||
|
|
<div class="filter-info">
|
|||
|
|
<?php if ($search && $category): ?>
|
|||
|
|
搜索"<strong><?php echo htmlspecialchars($search); ?></strong>",知识分类"<strong><?php echo htmlspecialchars($category); ?></strong>",共找到 <?php echo $total; ?> 条记录
|
|||
|
|
<?php elseif ($search): ?>
|
|||
|
|
搜索"<strong><?php echo htmlspecialchars($search); ?></strong>",共找到 <?php echo $total; ?> 条记录
|
|||
|
|
<?php elseif ($category): ?>
|
|||
|
|
分类"<strong><?php echo htmlspecialchars($category); ?></strong>",共 <?php echo $total; ?> 条记录
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<a href="?panel=Memo/manage-panel.php&scroll=list" class="clear-filter">清除筛选</a>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<?php if (empty($memos)): ?>
|
|||
|
|
<div class="empty-state">
|
|||
|
|
<div class="icon">📝</div>
|
|||
|
|
<h3>暂无知识记录</h3>
|
|||
|
|
<p>还没有添加知识,或者当前筛选条件没有匹配的记录</p>
|
|||
|
|
<?php if ($search || $category): ?>
|
|||
|
|
<a href="?panel=Memo/manage-panel.php&scroll=list" class="btn btn-primary" style="margin-top: 15px;">查看全部</a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<!-- 批量操作区域 -->
|
|||
|
|
<div class="bulk-actions">
|
|||
|
|
<div class="select-all-area">
|
|||
|
|
<div>
|
|||
|
|
<input type="checkbox" id="select-all" style="margin: 0; width: 16px; height: 16px;">
|
|||
|
|
<label for="select-all" style="color:#000;margin-left: 8px; font-weight: 500; font-size: 14px;">
|
|||
|
|
全选本页
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="selected-info">
|
|||
|
|
已选择 <strong id="selected-count">0</strong> 条记录
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="bulk-actions-buttons">
|
|||
|
|
<button type="button" class="btn btn-success btn-sm" onclick="exportSelected('txt')">
|
|||
|
|
导出选中TXT
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-warning btn-sm" onclick="exportSelected('md')">
|
|||
|
|
导出选中MD
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-danger btn-sm" onclick="confirmBulkDelete()">
|
|||
|
|
删除选中
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 主表单(只用于删除) -->
|
|||
|
|
<form method="post" action="" id="bulkActionForm" style="display:none;">
|
|||
|
|
<input type="hidden" name="bulk_delete" value="1">
|
|||
|
|
<input type="hidden" name="delete_ids[]" id="deleteIdsField">
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<!-- 导出表单 - 修复:改为两个独立的表单 -->
|
|||
|
|
<form method="post" action="" id="exportTxtForm" style="display:none;">
|
|||
|
|
<input type="hidden" name="export_ids" id="exportIdsTxtField">
|
|||
|
|
<input type="hidden" name="export_selected_txt" value="1">
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<form method="post" action="" id="exportMdForm" style="display:none;">
|
|||
|
|
<input type="hidden" name="export_ids" id="exportIdsMdField">
|
|||
|
|
<input type="hidden" name="export_selected_md" value="1">
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<table class="data-table">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>
|
|||
|
|
<input type="checkbox" id="thead-select-all" style="margin: 0; width: 16px; height: 16px;">
|
|||
|
|
</th>
|
|||
|
|
<th>ID</th>
|
|||
|
|
<th>知识内容</th>
|
|||
|
|
<th>知识分类</th>
|
|||
|
|
<th>发掘时间</th>
|
|||
|
|
<th>发布时间</th>
|
|||
|
|
<th>文章关联</th>
|
|||
|
|
<th>操作</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<?php foreach ($memos as $memo):
|
|||
|
|
$cids = !empty($memo['post_cids']) ? explode(',', $memo['post_cids']) : array();
|
|||
|
|
?>
|
|||
|
|
<tr>
|
|||
|
|
<td>
|
|||
|
|
<input type="checkbox" name="memo_ids[]" value="<?php echo $memo['id']; ?>" class="row-checkbox" style="margin: 0; width: 16px; height: 16px;">
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<span class="memo-id">#<?php echo $memo['id']; ?></span>
|
|||
|
|
</td>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<!-- <td>
|
|||
|
|
<div class="content-container">
|
|||
|
|
<div class="content-preview">
|
|||
|
|
<?php
|
|||
|
|
// 修复2:只对精确的URL加超链接
|
|||
|
|
$content = convertUrlsToLinks($memo['content']);
|
|||
|
|
|
|||
|
|
if ($search) {
|
|||
|
|
$searchEncoded = htmlspecialchars($search);
|
|||
|
|
$content = preg_replace(
|
|||
|
|
'/(' . preg_quote($searchEncoded, '/') . ')/i',
|
|||
|
|
'<mark style="background: #fff3cd; padding: 1px 3px;">$1</mark>',
|
|||
|
|
$content
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// 修复1:列表不处理换行,使用 nl2br 保持原有格式但不保留空格格式
|
|||
|
|
echo nl2br($content);
|
|||
|
|
?>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" class="view-more-btn"
|
|||
|
|
data-content="<?php echo htmlspecialchars($memo['content'], ENT_QUOTES, 'UTF-8'); ?>"
|
|||
|
|
data-original-url="<?php echo htmlspecialchars($memo['original_url'] ?? '', ENT_QUOTES, 'UTF-8'); ?>"
|
|||
|
|
onclick="viewContent(<?php echo $memo['id']; ?>, this)">
|
|||
|
|
查看更多
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</td>不管是否截取均显示查看更多-->
|
|||
|
|
<td>
|
|||
|
|
<div class="content-container">
|
|||
|
|
<div class="content-preview" id="content-preview-<?php echo $memo['id']; ?>">
|
|||
|
|
<?php
|
|||
|
|
// 修复2:只对精确的URL加超链接
|
|||
|
|
$content = convertUrlsToLinks($memo['content']);
|
|||
|
|
|
|||
|
|
if ($search) {
|
|||
|
|
$searchEncoded = htmlspecialchars($search);
|
|||
|
|
$content = preg_replace(
|
|||
|
|
'/(' . preg_quote($searchEncoded, '/') . ')/i',
|
|||
|
|
'<mark style="background: #fff3cd; padding: 1px 3px;">$1</mark>',
|
|||
|
|
$content
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取纯文本长度(不含HTML标签)用于判断
|
|||
|
|
$plainText = strip_tags($content);
|
|||
|
|
|
|||
|
|
// 显示内容,如果过长则截取
|
|||
|
|
$maxLength = 200; // 截取长度阈值
|
|||
|
|
$showFullContent = false;
|
|||
|
|
|
|||
|
|
if (mb_strlen($plainText, 'UTF-8') > $maxLength) {
|
|||
|
|
// 截取内容
|
|||
|
|
$shortenedContent = mb_substr($plainText, 0, $maxLength, 'UTF-8') . '...';
|
|||
|
|
// 重新应用HTML处理(只处理URL)
|
|||
|
|
$shortenedContent = convertUrlsToLinks($shortenedContent);
|
|||
|
|
|
|||
|
|
// 如果搜索关键词被截断了,确保高亮显示
|
|||
|
|
if ($search) {
|
|||
|
|
$shortenedContent = preg_replace(
|
|||
|
|
'/(' . preg_quote(htmlspecialchars($search), '/') . ')/i',
|
|||
|
|
'<mark style="background: #fff3cd; padding: 1px 3px;">$1</mark>',
|
|||
|
|
$shortenedContent
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
echo nl2br($shortenedContent);
|
|||
|
|
$showFullContent = true;
|
|||
|
|
} else {
|
|||
|
|
// 内容不长,显示完整内容
|
|||
|
|
echo nl2br($content);
|
|||
|
|
$showFullContent = false;
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<?php if ($showFullContent): ?>
|
|||
|
|
<button type="button" class="view-more-btn"
|
|||
|
|
data-content="<?php echo htmlspecialchars($memo['content'], ENT_QUOTES, 'UTF-8'); ?>"
|
|||
|
|
data-original-url="<?php echo htmlspecialchars($memo['original_url'] ?? '', ENT_QUOTES, 'UTF-8'); ?>"
|
|||
|
|
onclick="viewContent(<?php echo $memo['id']; ?>, this)">
|
|||
|
|
查看更多
|
|||
|
|
</button>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
</td>
|
|||
|
|
|
|||
|
|
<td>
|
|||
|
|
<a href="?panel=Memo/manage-panel.php&category=<?php echo urlencode($memo['category']); ?>&scroll=list"
|
|||
|
|
class="category-badge"
|
|||
|
|
title="点击查看该分类的知识">
|
|||
|
|
<?php echo htmlspecialchars($memo['category']); ?>
|
|||
|
|
</a>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<?php if (!empty($memo['event_date'])): ?>
|
|||
|
|
<span class="date-badge">
|
|||
|
|
<?php echo date('Y-m-d', strtotime($memo['event_date'])); ?>
|
|||
|
|
</span>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<span style="color: #999; font-size: 12px;">-</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<span class="created-time">
|
|||
|
|
<?php echo date('Y-m-d', strtotime($memo['created'])); ?>
|
|||
|
|
</span>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<?php if (!empty($cids)): ?>
|
|||
|
|
<div class="cid-container">
|
|||
|
|
<?php foreach ($cids as $cid):
|
|||
|
|
$cid = trim($cid);
|
|||
|
|
if (!empty($cid)):
|
|||
|
|
$postInfo = $memo['post_info'][$cid] ?? null;
|
|||
|
|
$title = $postInfo['title'] ?? '文章' . $cid;
|
|||
|
|
$url = $postInfo['url'] ?? '';
|
|||
|
|
?>
|
|||
|
|
<?php if (!empty($url)): ?>
|
|||
|
|
<a href="<?php echo htmlspecialchars($url); ?>"
|
|||
|
|
class="cid-badge"
|
|||
|
|
title="<?php echo htmlspecialchars($title); ?>"
|
|||
|
|
target="_blank">
|
|||
|
|
<?php echo $cid; ?>
|
|||
|
|
</a>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<span class="cid-badge" title="<?php echo htmlspecialchars($title); ?>">
|
|||
|
|
<?php echo $cid; ?>
|
|||
|
|
</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<span style="color: #999; font-size: 12px;">-</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<div class="action-buttons">
|
|||
|
|
<!-- 新增:单个导出按钮 -->
|
|||
|
|
<form method="post" action="" style="display: inline;">
|
|||
|
|
<input type="hidden" name="single_id" value="<?php echo $memo['id']; ?>">
|
|||
|
|
<button type="submit" name="export_single_txt" class="btn btn-sm btn-success" style="padding: 4px 8px; font-size: 12px;" title="导出为TXT">
|
|||
|
|
TXT
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
<form method="post" action="" style="display: inline;">
|
|||
|
|
<input type="hidden" name="single_id" value="<?php echo $memo['id']; ?>">
|
|||
|
|
<button type="submit" name="export_single_md" class="btn btn-sm btn-warning" style="padding: 4px 8px; font-size: 12px;" title="导出为MD">
|
|||
|
|
MD
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
<button type="button" class="btn btn-sm btn-primary edit-btn"
|
|||
|
|
data-memo='<?php echo htmlspecialchars(json_encode($memo), ENT_QUOTES, 'UTF-8'); ?>'
|
|||
|
|
style="padding: 4px 10px; font-size: 12px;" title="编辑">
|
|||
|
|
编辑
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<!-- 批量操作底部栏 -->
|
|||
|
|
<div class="bulk-actions bulk-actions-footer">
|
|||
|
|
<div class="select-all-area">
|
|||
|
|
<div>
|
|||
|
|
<input type="checkbox" id="footer-select-all" style="margin: 0; width: 16px; height: 16px;">
|
|||
|
|
<label for="footer-select-all" style="color:#000;margin-left: 8px; font-weight: 500; font-size: 14px;">
|
|||
|
|
全选本页
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="selected-info">
|
|||
|
|
已选择 <strong id="footer-selected-count">0</strong> 条记录
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="bulk-actions-buttons">
|
|||
|
|
<button type="button" class="btn btn-success btn-sm" onclick="exportSelected('txt')">
|
|||
|
|
导出选中TXT
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-warning btn-sm" onclick="exportSelected('md')">
|
|||
|
|
导出选中MD
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-danger btn-sm" onclick="confirmBulkDelete()">
|
|||
|
|
删除选中
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 分页 -->
|
|||
|
|
<?php if ($totalPages > 1): ?>
|
|||
|
|
<div class="pagination">
|
|||
|
|
<ul>
|
|||
|
|
<?php if ($page > 1): ?>
|
|||
|
|
<li><a href="?panel=Memo/manage-panel.php&page=<?php echo $page-1; ?><?php echo $search ? '&search=' . urlencode($search) : ''; ?><?php echo $category ? '&category=' . urlencode($category) : ''; ?>&scroll=list">« 上一页</a></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php
|
|||
|
|
$start = max(1, $page - 2);
|
|||
|
|
$end = min($totalPages, $page + 2);
|
|||
|
|
|
|||
|
|
if ($start > 1) {
|
|||
|
|
echo '<li><a href="?panel=Memo/manage-panel.php&page=1' . ($search ? '&search=' . urlencode($search) : '') . ($category ? '&category=' . urlencode($category) : '') . '&scroll=list">1</a></li>';
|
|||
|
|
if ($start > 2) echo '<li><span>...</span></li>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for ($i = $start; $i <= $end; $i++): ?>
|
|||
|
|
<li <?php if ($i == $page) echo 'class="active"'; ?>>
|
|||
|
|
<a href="?panel=Memo/manage-panel.php&page=<?php echo $i; ?><?php echo $search ? '&search=' . urlencode($search) : ''; ?><?php echo $category ? '&category=' . urlencode($category) : ''; ?>&scroll=list"><?php echo $i; ?></a>
|
|||
|
|
</li>
|
|||
|
|
<?php endfor; ?>
|
|||
|
|
|
|||
|
|
<?php if ($end < $totalPages): ?>
|
|||
|
|
<?php if ($end < $totalPages - 1): ?>
|
|||
|
|
<li><span>...</span></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<li><a href="?panel=Memo/manage-panel.php&page=<?php echo $totalPages; ?><?php echo $search ? '&search=' . urlencode($search) : ''; ?><?php echo $category ? '&category=' . urlencode($category) : ''; ?>&scroll=list"><?php echo $totalPages; ?></a></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if ($page < $totalPages): ?>
|
|||
|
|
<li><a href="?panel=Memo/manage-panel.php&page=<?php echo $page+1; ?><?php echo $search ? '&search=' . urlencode($search) : ''; ?><?php echo $category ? '&category=' . urlencode($category) : ''; ?>&scroll=list">下一页 »</a></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 编辑模态框 -->
|
|||
|
|
<div id="editModal" class="modal" style="display:none;">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<button type="button" class="close" onclick="closeModal()">×</button>
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h3>编辑知识</h3>
|
|||
|
|
</div>
|
|||
|
|
<form id="editForm" method="post" action="">
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_category">知识分类 *</label>
|
|||
|
|
<select id="edit_category" name="edit_category" class="form-control" required>
|
|||
|
|
<option value="">请选择分类</option>
|
|||
|
|
<?php foreach ($defaultCategories as $cat): ?>
|
|||
|
|
<option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
<?php foreach ($allCategories as $cat): ?>
|
|||
|
|
<?php if (!in_array($cat, $defaultCategories)): ?>
|
|||
|
|
<option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_event_date">发掘日期(可选)</label>
|
|||
|
|
<input type="date" id="edit_event_date" name="edit_event_date" class="form-control">
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_content">知识内容 *</label>
|
|||
|
|
<textarea id="edit_content" name="edit_content" class="form-control" rows="5" required></textarea>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-row">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_post_cids">关联文章CID(可选)</label>
|
|||
|
|
<input type="text" id="edit_post_cids" name="edit_post_cids" class="form-control"
|
|||
|
|
placeholder="输入文章CID,多个用逗号分隔">
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_original_url">原文链接(可选)</label>
|
|||
|
|
<input type="text" id="edit_original_url" name="edit_original_url" class="form-control"
|
|||
|
|
placeholder="输入原文链接">
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<input type="hidden" name="edit_id" id="edit_id">
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="text-align:right;margin-top:0; padding: 15px 25px; border-top: 1px solid #eee;">
|
|||
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal()" style="margin-right:10px;">
|
|||
|
|
取消
|
|||
|
|
</button>
|
|||
|
|
<button type="submit" class="btn btn-primary">保存修改</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 内容查看模态框 -->
|
|||
|
|
<div id="contentModal" class="modal" style="display:none;">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<button type="button" class="close" onclick="closeContentModal()">×</button>
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h3>知识内容</h3>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body" id="contentModalBody">
|
|||
|
|
<!-- 内容将通过JavaScript填充 -->
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// 页面加载后自动滚动到锚点
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
// 检查是否需要滚动到列表区域
|
|||
|
|
var urlParams = new URLSearchParams(window.location.search);
|
|||
|
|
if (urlParams.get('scroll') === 'list') {
|
|||
|
|
setTimeout(function() {
|
|||
|
|
var anchor = document.getElementById('memo-list-anchor');
|
|||
|
|
if (anchor) {
|
|||
|
|
anchor.scrollIntoView({ behavior: 'smooth' });
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 自动隐藏浮动消息
|
|||
|
|
var floatingMessage = document.getElementById('floatingMessage');
|
|||
|
|
if (floatingMessage) {
|
|||
|
|
setTimeout(function() {
|
|||
|
|
hideMessage();
|
|||
|
|
}, 5000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化选择功能
|
|||
|
|
initBulkOperations();
|
|||
|
|
|
|||
|
|
// 初始化编辑按钮
|
|||
|
|
initEditButtons();
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
initFormValidation();
|
|||
|
|
|
|||
|
|
// 搜索框回车提交
|
|||
|
|
var searchInput = document.querySelector('.search-input');
|
|||
|
|
if (searchInput) {
|
|||
|
|
searchInput.addEventListener('keypress', function(e) {
|
|||
|
|
if (e.key === 'Enter') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
// 添加滚动参数
|
|||
|
|
var form = this.form;
|
|||
|
|
var scrollInput = form.querySelector('input[name="scroll"]');
|
|||
|
|
if (!scrollInput) {
|
|||
|
|
scrollInput = document.createElement('input');
|
|||
|
|
scrollInput.type = 'hidden';
|
|||
|
|
scrollInput.name = 'scroll';
|
|||
|
|
scrollInput.value = 'list';
|
|||
|
|
form.appendChild(scrollInput);
|
|||
|
|
}
|
|||
|
|
form.submit();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文件上传类型变化时显示/隐藏SQL选项
|
|||
|
|
var importFile = document.getElementById('import_file');
|
|||
|
|
var sqlImportOptions = document.getElementById('sqlImportOptions');
|
|||
|
|
|
|||
|
|
if (importFile && sqlImportOptions) {
|
|||
|
|
importFile.addEventListener('change', function() {
|
|||
|
|
var fileName = this.value.toLowerCase();
|
|||
|
|
if (fileName.endsWith('.sql')) {
|
|||
|
|
sqlImportOptions.style.display = 'block';
|
|||
|
|
} else {
|
|||
|
|
sqlImportOptions.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 隐藏浮动消息
|
|||
|
|
function hideMessage() {
|
|||
|
|
var message = document.getElementById('floatingMessage');
|
|||
|
|
if (message) {
|
|||
|
|
message.classList.remove('show');
|
|||
|
|
setTimeout(function() {
|
|||
|
|
if (message.parentNode) {
|
|||
|
|
message.parentNode.removeChild(message);
|
|||
|
|
}
|
|||
|
|
}, 300);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将文本中的URL转换为链接(JavaScript版本)
|
|||
|
|
function makeLinksClickable(text) {
|
|||
|
|
if (!text) return text;
|
|||
|
|
|
|||
|
|
// 精确匹配URL的正则表达式
|
|||
|
|
var urlPattern = /(https?:\/\/[a-zA-Z0-9][-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/g;
|
|||
|
|
|
|||
|
|
return text.replace(urlPattern, function(url) {
|
|||
|
|
// 清理URL末尾可能错误的标点符号
|
|||
|
|
var punctuation = ['.', ',', ';', ':', '!', '?', ')', ']', '}'];
|
|||
|
|
var lastChar = url.slice(-1);
|
|||
|
|
var suffix = '';
|
|||
|
|
|
|||
|
|
if (punctuation.includes(lastChar)) {
|
|||
|
|
url = url.slice(0, -1);
|
|||
|
|
suffix = lastChar;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建链接
|
|||
|
|
return '<a href="' + url + '" class="url-link" target="_blank" rel="noopener noreferrer">' + url + '</a>' + suffix;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查看完整内容
|
|||
|
|
function viewContent(memoId, button) {
|
|||
|
|
var content = button.getAttribute('data-content');
|
|||
|
|
var originalUrl = button.getAttribute('data-original-url');
|
|||
|
|
|
|||
|
|
// 处理内容中的换行符
|
|||
|
|
content = content.replace(/\\r\\n|\\r|\\n/g, '\n');
|
|||
|
|
|
|||
|
|
// 处理内容中的URL链接
|
|||
|
|
var processedContent = makeLinksClickable(content);
|
|||
|
|
|
|||
|
|
// 创建模态框内容
|
|||
|
|
var modalContent = '<div style="margin-bottom: 15px; font-size: 12px; color: #666;">';
|
|||
|
|
modalContent += '知识 ID: #' + memoId;
|
|||
|
|
modalContent += '</div>';
|
|||
|
|
modalContent += '<div style="font-size: 14px; line-height: 1.6; color: #333; white-space: pre-wrap; word-wrap: break-word; margin-bottom: 20px;">';
|
|||
|
|
modalContent += processedContent.replace(/\n/g, '<br>');
|
|||
|
|
modalContent += '</div>';
|
|||
|
|
|
|||
|
|
// 添加原文链接(如果有)
|
|||
|
|
if (originalUrl && originalUrl.trim() !== '') {
|
|||
|
|
modalContent += '<div class="original-link">';
|
|||
|
|
modalContent += '<div class="original-link-label">原文链接:</div>';
|
|||
|
|
modalContent += '<div class="original-link-url">';
|
|||
|
|
modalContent += '<a href="' + originalUrl + '" target="_blank" rel="noopener noreferrer">' + originalUrl + '</a>';
|
|||
|
|
modalContent += '</div>';
|
|||
|
|
modalContent += '</div>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新模态框内容
|
|||
|
|
document.getElementById('contentModalBody').innerHTML = modalContent;
|
|||
|
|
|
|||
|
|
// 显示模态框
|
|||
|
|
document.getElementById('contentModal').style.display = 'block';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 关闭内容查看模态框
|
|||
|
|
function closeContentModal() {
|
|||
|
|
document.getElementById('contentModal').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 点击模态框外部关闭
|
|||
|
|
window.onclick = function(event) {
|
|||
|
|
var modal = document.getElementById('editModal');
|
|||
|
|
var contentModal = document.getElementById('contentModal');
|
|||
|
|
|
|||
|
|
if (event.target == modal) {
|
|||
|
|
closeModal();
|
|||
|
|
}
|
|||
|
|
if (event.target == contentModal) {
|
|||
|
|
closeContentModal();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化选择功能
|
|||
|
|
function initBulkOperations() {
|
|||
|
|
// 更新选中数量
|
|||
|
|
function updateSelectedCount() {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
|||
|
|
var count = checkboxes.length;
|
|||
|
|
|
|||
|
|
document.getElementById('selected-count').textContent = count;
|
|||
|
|
document.getElementById('footer-selected-count').textContent = count;
|
|||
|
|
|
|||
|
|
return count;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 全选/全不选
|
|||
|
|
function setupSelectAll(selector, targetSelector) {
|
|||
|
|
var selectAll = document.querySelector(selector);
|
|||
|
|
if (selectAll) {
|
|||
|
|
selectAll.addEventListener('change', function(e) {
|
|||
|
|
var checkboxes = document.querySelectorAll(targetSelector);
|
|||
|
|
checkboxes.forEach(function(checkbox) {
|
|||
|
|
checkbox.checked = e.target.checked;
|
|||
|
|
});
|
|||
|
|
updateSelectedCount();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 行复选框事件
|
|||
|
|
function setupRowCheckboxes() {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox');
|
|||
|
|
checkboxes.forEach(function(checkbox) {
|
|||
|
|
checkbox.addEventListener('change', updateSelectedCount);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置事件监听
|
|||
|
|
setupSelectAll('#select-all', '.row-checkbox');
|
|||
|
|
setupSelectAll('#thead-select-all', '.row-checkbox');
|
|||
|
|
setupSelectAll('#footer-select-all', '.row-checkbox');
|
|||
|
|
setupRowCheckboxes();
|
|||
|
|
|
|||
|
|
updateSelectedCount();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 导出选中记录
|
|||
|
|
function exportSelected(format) {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
|||
|
|
var count = checkboxes.length;
|
|||
|
|
|
|||
|
|
if (count === 0) {
|
|||
|
|
alert('请先选择要导出的知识!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!confirm('确定要导出选中的 ' + count + ' 条知识吗?')) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 收集选中的ID
|
|||
|
|
var selectedIds = [];
|
|||
|
|
checkboxes.forEach(function(checkbox) {
|
|||
|
|
selectedIds.push(checkbox.value);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 设置导出表单数据并提交
|
|||
|
|
if (format === 'txt') {
|
|||
|
|
document.getElementById('exportIdsTxtField').value = selectedIds.join(',');
|
|||
|
|
document.getElementById('exportTxtForm').submit();
|
|||
|
|
} else if (format === 'md') {
|
|||
|
|
document.getElementById('exportIdsMdField').value = selectedIds.join(',');
|
|||
|
|
document.getElementById('exportMdForm').submit();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认批量删除
|
|||
|
|
function confirmBulkDelete() {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
|||
|
|
var count = checkboxes.length;
|
|||
|
|
|
|||
|
|
if (count === 0) {
|
|||
|
|
alert('请先选择要删除的知识!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!confirm('确定要删除选中的 ' + count + ' 条知识吗?此操作不可恢复!')) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 收集选中的ID
|
|||
|
|
var selectedIds = [];
|
|||
|
|
checkboxes.forEach(function(checkbox) {
|
|||
|
|
selectedIds.push(checkbox.value);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 设置删除表单数据并提交
|
|||
|
|
document.getElementById('deleteIdsField').value = selectedIds.join(',');
|
|||
|
|
document.getElementById('bulkActionForm').submit();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化编辑按钮
|
|||
|
|
function initEditButtons() {
|
|||
|
|
var editButtons = document.querySelectorAll('.edit-btn');
|
|||
|
|
editButtons.forEach(function(button) {
|
|||
|
|
button.addEventListener('click', function() {
|
|||
|
|
try {
|
|||
|
|
var memoData = JSON.parse(this.getAttribute('data-memo'));
|
|||
|
|
editMemo(memoData);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('解析知识数据失败:', e);
|
|||
|
|
alert('编辑失败:数据格式错误');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 编辑知识
|
|||
|
|
function editMemo(memo) {
|
|||
|
|
document.getElementById('edit_id').value = memo.id;
|
|||
|
|
document.getElementById('edit_content').value = memo.content;
|
|||
|
|
|
|||
|
|
// 设置分类
|
|||
|
|
var categorySelect = document.getElementById('edit_category');
|
|||
|
|
for (var i = 0; i < categorySelect.options.length; i++) {
|
|||
|
|
if (categorySelect.options[i].value === memo.category) {
|
|||
|
|
categorySelect.selectedIndex = i;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置事件日期
|
|||
|
|
if (memo.event_date) {
|
|||
|
|
var eventDate = new Date(memo.event_date);
|
|||
|
|
var year = eventDate.getFullYear();
|
|||
|
|
var month = (eventDate.getMonth() + 1).toString().padStart(2, '0');
|
|||
|
|
var day = eventDate.getDate().toString().padStart(2, '0');
|
|||
|
|
document.getElementById('edit_event_date').value = year + '-' + month + '-' + day;
|
|||
|
|
} else {
|
|||
|
|
document.getElementById('edit_event_date').value = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置关联文章CID
|
|||
|
|
document.getElementById('edit_post_cids').value = memo.post_cids || '';
|
|||
|
|
|
|||
|
|
// 设置原文链接
|
|||
|
|
document.getElementById('edit_original_url').value = memo.original_url || '';
|
|||
|
|
|
|||
|
|
// 显示模态框
|
|||
|
|
document.getElementById('editModal').style.display = 'block';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 关闭模态框
|
|||
|
|
function closeModal() {
|
|||
|
|
document.getElementById('editModal').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
function initFormValidation() {
|
|||
|
|
var publishForm = document.getElementById('publishForm');
|
|||
|
|
if (publishForm) {
|
|||
|
|
publishForm.addEventListener('submit', function(e) {
|
|||
|
|
var category = document.getElementById('category').value;
|
|||
|
|
var content = document.getElementById('content').value;
|
|||
|
|
|
|||
|
|
if (!category) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('请选择分类!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!content.trim()) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('请输入内容!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var editForm = document.getElementById('editForm');
|
|||
|
|
if (editForm) {
|
|||
|
|
editForm.addEventListener('submit', function(e) {
|
|||
|
|
var category = document.getElementById('edit_category').value;
|
|||
|
|
var content = document.getElementById('edit_content').value;
|
|||
|
|
|
|||
|
|
if (!category) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('请选择分类!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!content.trim()) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('请输入内容!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<?php include 'footer.php'; ?>
|