| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>知识笔记管理系统</title>
-
- <!-- Bootstrap CSS -->
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
-
- <!-- Vditor CSS -->
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vditor/dist/index.css">
-
- <!-- Font Awesome -->
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
- <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
- integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
- crossorigin="anonymous" referrerpolicy="no-referrer"></script>
-
- <style>
- .hidden {
- display: none;
- }
-
- .editor-shadow {
- box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
- border-radius: 8px;
- padding: 20px;
- margin-bottom: 20px;
- }
-
- .progress-section {
- background-color: #f8f9fa;
- border-radius: 8px;
- padding: 15px;
- margin: 15px 0;
- }
-
- .upload-btn {
- background-color: #0d6efd;
- color: white;
- border: none;
- padding: 10px 20px;
- border-radius: 5px;
- cursor: pointer;
- transition: background-color 0.3s;
- }
-
- .upload-btn:hover {
- background-color: #0b5ed7;
- }
-
- .upload-btn:disabled {
- background-color: #6c757d;
- cursor: not-allowed;
- }
-
- .file-preview {
- max-height: 150px;
- max-width: 100%;
- margin-top: 10px;
- border: 1px solid #dee2e6;
- border-radius: 5px;
- }
-
- .form-section {
- margin-bottom: 25px;
- padding: 20px;
- border: 1px solid #e9ecef;
- border-radius: 8px;
- }
-
- .section-title {
- font-size: 1.2rem;
- font-weight: 600;
- margin-bottom: 15px;
- color: #495057;
- border-bottom: 1px solid #e9ecef;
- padding-bottom: 10px;
- }
- </style>
- </head>
- <body>
- <div class="container-fluid">
- <div class="row">
- <!-- 侧边导航 -->
- <nav class="col-md-2 d-md-block bg-light sidebar">
- <div class="sidebar-sticky pt-3">
- <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
- <span>知识管理</span>
- </h6>
- <ul class="nav flex-column">
- <li class="nav-item">
- <a class="nav-link active" href="/knowledgenotemanage">
- <i class="fas fa-book mr-2"></i> 笔记管理
- </a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="/newknowledgenote">
- <i class="fas fa-plus-circle mr-2"></i> 添加笔记
- </a>
- </li>
- </ul>
- </div>
- </nav>
- <!-- 主内容区域 -->
- <main role="main" class="col-md-10 ml-sm-auto px-4">
- <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">
- {% if status=='add' %}
- 添加知识笔记
- {% elif status=='edit' %}
- 编辑知识笔记
- {% endif %}
- </h1>
- </div>
- <!-- 消息提示 -->
- {% with messages = get_flashed_messages() %}
- {% if messages %}
- <div class="alert-container">
- {% for message in messages %}
- <div class="alert alert-info alert-dismissible fade show" role="alert">
- {{ message }}
- <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
- </div>
- {% endfor %}
- </div>
- {% endif %}
- {% endwith %}
- <!-- 笔记编辑表单 -->
- <form action="/editknowledgenote.do" method="post" enctype="multipart/form-data" onsubmit="return prepareFormSubmission()">
- <input type="hidden" name="lbl_status" value="{{ status }}"/>
- <!-- 基本信息部分 -->
- <div class="form-section">
- <div class="section-title">基本信息</div>
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">所属知识专题</label>
- {% if status=='add' %}
- <select name="txt_KTID" class="form-control" required>
- <option value="">请选择知识专题</option>
- {% for row1 in knowledgetopic %}
- <option value="{{row1[0]}}">{{row1[2]}}</option>
- {% endfor %}
- </select>
- {% elif status=='edit' and knowledgenote %}
- <input type="hidden" name="lbl_KNID" value="{{ knowledgenote[0] }}"/>
- <select name="txt_KTID" class="form-control" required>
- {% for row in knowledgetopic %}
- <option value="{{row[0]}}" {% if knowledgenote[1] == row[0] %}selected{% endif %}>{{row[2]}}</option>
- {% endfor %}
- </select>
- {% endif %}
- </div>
- </div>
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">笔记标题</label>
- <input name="txt_KNTitle" type="text" class="form-control" placeholder="填写笔记标题"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[2]}}" {% endif %} required>
- </div>
- </div>
- </div>
-
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">原创/转载</label>
- <div class="form-check form-check-inline">
- <input class="form-check-input" name="txt_IsOriginal" type="radio" value="1" id="original"
- {% if status=='add' %}checked{% endif %}
- {% if status=='edit' and knowledgenote and knowledgenote[3]==1 %}checked{% endif %}>
- <label class="form-check-label" for="original">原创</label>
- </div>
- <div class="form-check form-check-inline">
- <input class="form-check-input" name="txt_IsOriginal" type="radio" value="2" id="repost"
- {% if status=='edit' and knowledgenote and knowledgenote[3]==2 %}checked{% endif %}>
- <label class="form-check-label" for="repost">转载</label>
- </div>
- </div>
-
- <div class="mb-3">
- <label class="form-label">来源网站</label>
- <input name="txt_Source" type="text" class="form-control" placeholder="填写转载来源网站名称"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[4]}}" {% endif %}>
- </div>
-
- <div class="mb-3">
- <label class="form-label">网站链接</label>
- <input name="txt_OrigLink" type="text" class="form-control" placeholder="填写转载来源链接"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[5]}}" {% endif %}>
- </div>
- </div>
-
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">封面图片</label>
- <input id="file_proimg" name="file_proimg" type="file" class="form-control" accept="image/*">
- <input id="txt_KNImage" name="txt_KNImage" type="hidden"
- {% if status =='edit' and knowledgenote %} value="{{knowledgenote[6]}}" {% else %} value="" {% endif %}/>
- <div id="imagePreview" class="mt-2">
- {% if status =='edit' and knowledgenote and knowledgenote[6] %}
- <img src="/{{knowledgenote[6]}}" class="file-preview">
- {% endif %}
- </div>
- </div>
- </div>
- </div>
-
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">作者</label>
- <input name="txt_CoAuthors" type="text" class="form-control" placeholder="填写作者"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[7]}}" {% endif %}>
- </div>
- </div>
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">是否显示</label>
- <select name="txt_KNIsShow" class="form-control">
- <option value="1" {% if status=='add' %}selected{% endif %}
- {% if status=='edit' and knowledgenote and knowledgenote[15]==1 %}selected{% endif %}>显示</option>
- <option value="0" {% if status=='edit' and knowledgenote and knowledgenote[15]==0 %}selected{% endif %}>不显示</option>
- </select>
- </div>
- </div>
- </div>
-
- <div class="mb-3">
- <label class="form-label">摘要</label>
- <textarea name="txt_KNAbstract" class="form-control" rows="3" placeholder="填写笔记摘要">{% if status=='edit' and knowledgenote %}{{knowledgenote[8]}}{% endif %}</textarea>
- </div>
- </div>
- <!-- 内容编辑部分 -->
- <div class="form-section">
- <div class="section-title">笔记内容</div>
-
- <!-- MD文件上传区域 -->
- <div class="mb-3">
- <button type="button" id="packButton" class="upload-btn" onclick="uploadMdFile()">
- <i class="fas fa-folder-open mr-2"></i>选择文件夹并打包上传
- </button>
-
- <!-- 进度显示区域 -->
- <div id="progressSection" class="progress-section hidden">
- <div class="d-flex justify-content-between align-items-center mb-2">
- <h5 class="mb-0">处理进度</h5>
- <span id="progressPercent" class="fw-bold">0%</span>
- </div>
- <div class="progress mb-2">
- <div id="progressBar" class="progress-bar" role="progressbar" style="width: 0%"></div>
- </div>
- <p id="progressText" class="mb-0 text-muted">准备处理文件...</p>
- </div>
- </div>
-
- <!-- Vditor编辑器 -->
- <input type="hidden" id="txt_KNcontent" name="txt_KNcontent"/>
- <div id="vditor"></div>
- </div>
- <!-- 附件部分 -->
- <div class="form-section">
- <div class="section-title">附件信息</div>
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">视频文件附件</label>
- <input name="txt_VideoLink" type="text" class="form-control" placeholder="视频文件路径/static/uploads/video_notes/"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[12]}}" {% endif %}>
- <input id="file_video" name="file_video" type="file" class="form-control mt-2" accept="video/*">
- </div>
- </div>
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">文件附件</label>
- <input name="txt_FileLink" type="text" class="form-control" placeholder="文件路径"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[10]}}" {% endif %}>
- </div>
- </div>
- </div>
-
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">PPT文件附件</label>
- <input name="txt_PPTLink" type="text" class="form-control" placeholder="PPT文件路径"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[11]}}" {% endif %}>
- </div>
- </div>
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">代码文件附件</label>
- <input name="txt_CodeLink" type="text" class="form-control" placeholder="代码文件路径"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[13]}}" {% endif %}>
- </div>
- </div>
- </div>
-
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <label class="form-label">数据文件附件</label>
- <input name="txt_DataLink" type="text" class="form-control" placeholder="数据文件路径"
- {% if status=='edit' and knowledgenote %} value="{{knowledgenote[14]}}" {% endif %}>
- </div>
- </div>
- </div>
- </div>
- <!-- 操作按钮 -->
- <div class="d-flex justify-content-end gap-2 mt-4">
- <button type="submit" class="btn btn-primary">
- <i class="fas fa-save mr-2"></i>保存笔记
- </button>
- <a href="/knowledgenotemanage" class="btn btn-secondary">
- <i class="fas fa-arrow-left mr-2"></i>返回列表
- </a>
- </div>
- </form>
- </main>
- </div>
- </div>
- <!-- Bootstrap JS -->
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
-
- <!-- Vditor JS -->
- <script src="https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js"></script>
-
- <!-- JSZip -->
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
- <script>
- $(function(){
- $("#file_proimg").change(function(){
- //alert($(this).val());
- $("#txt_RTImage").val($(this).val());
- var objUrl=getObjectURL(this.files[0]);
- //console.log("objUrl="+objUrl);
- if(objUrl){
- $("#img_proimg").attr("src",objUrl);
- $("#file_proimg").attr("src",objUrl);
- }
- })
- //建立一個可存取到該file的url
- function getObjectURL(file) {
- var url = null ;
- if (window.createObjectURL!=undefined) {
- url = window.createObjectURL(file) ;
- } else if (window.URL!=undefined) {
- url = window.URL.createObjectURL(file) ;
- } else if (window.webkitURL!=undefined) {
- url = window.webkitURL.createObjectURL(file) ;
- }
- return url ;
- }
- })
- //选择视频
- $(function(){
- $("#file_video").change(function(){
- //alert($(this).val());
- $("#txt_VideoLink").val($(this).val());
- var objUrl=getObjectURL(this.files[0]);
- //console.log("objUrl="+objUrl);
- if(objUrl){
- $("#file_video").attr("src",objUrl);
- }
- })
- //建立一個可存取到該file的url
- function getObjectURL(file) {
- var url = null ;
- if (window.createObjectURL!=undefined) {
- url = window.createObjectURL(file) ;
- } else if (window.URL!=undefined) {
- url = window.URL.createObjectURL(file) ;
- } else if (window.webkitURL!=undefined) {
- url = window.webkitURL.createObjectURL(file) ;
- }
- return url ;
- }
- })
- function getV() {
- $("#txt_KNcontent").val(JSON.stringify(vditor.getValue()))
- }
- // 全局变量
- let vditorInstance;
- let currentMdContent = '';
-
- // 页面加载完成后初始化
- document.addEventListener('DOMContentLoaded', function() {
- // 初始化Vditor编辑器
- initVditor();
-
- // 绑定文件预览事件
- bindFilePreviewEvents();
- });
-
- // 初始化Vditor编辑器
- function initVditor() {
- vditorInstance = new Vditor('vditor', {
- height: 600,
- cache: {
- enable: false
- },
- value: '{% if status=="edit" and knowledgenote and knowledgenote[9] %}{{ knowledgenote[9] | tojson }}{% else %}{% endif %}',
- mode: 'wysiwyg',
- theme: 'classic',
- icon: 'material',
- upload: {
- url: '/vditor_web/uploads',
- linkToImgUrl: '/static/uploads/img_comms/',
- accept: '.jpg,.png,.gif,.jpeg',
- filename: function(name) {
- return name.replace(/\?|\\|\/|:|\||<|>|\*|\[|\]|\s+/g, '-');
- },
- },
- after: function() {
- console.log('Vditor编辑器初始化完成');
- }
- });
- }
-
- // 绑定文件预览事件
- function bindFilePreviewEvents() {
- // 封面图片预览
- const fileProimg = document.getElementById('file_proimg');
- if (fileProimg) {
- fileProimg.addEventListener('change', function(e) {
- const file = e.target.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = function(e) {
- const preview = document.getElementById('imagePreview');
- preview.innerHTML = `<img src="${e.target.result}" class="file-preview">`;
- };
- reader.readAsDataURL(file);
- }
- });
- }
- }
-
- // 准备表单提交
- function prepareFormSubmission() {
- if (vditorInstance) {
- const content = vditorInstance.getValue();
- document.getElementById('txt_KNcontent').value = JSON.stringify(content);
- }
- return true;
- }
-
- // 上传MD文件函数
- async function uploadMdFile() {
- try {
- // 检查浏览器是否支持文件夹选择
- if (!window.showDirectoryPicker) {
- alert('您的浏览器不支持文件夹选择功能,请使用Chrome或Edge等现代浏览器');
- return;
- }
-
- // 选择文件夹
- const directoryHandle = await window.showDirectoryPicker();
- const button = document.getElementById('packButton');
- if (button) {
- button.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>正在打包中...';
- button.disabled = true;
- }
-
- // 显示进度区域
- const progressSection = document.getElementById('progressSection');
- const progressBar = document.getElementById('progressBar');
- const progressPercent = document.getElementById('progressPercent');
- const progressText = document.getElementById('progressText');
-
- progressSection.classList.remove('hidden');
- progressBar.style.width = '10%';
- progressPercent.textContent = '10%';
- progressText.textContent = '正在读取文件夹内容...';
-
- // 获取所有文件
- const files = await getAllFiles(directoryHandle);
-
- if (files.length === 0) {
- alert('选择的文件夹为空');
- resetUploadButton(button, progressSection);
- return;
- }
-
- // 更新进度
- progressBar.style.width = '30%';
- progressPercent.textContent = '30%';
- progressText.textContent = `找到 ${files.length} 个文件,正在打包...`;
-
- // 创建ZIP文件
- const zip = new JSZip();
-
- for (const fileInfo of files) {
- const file = await fileInfo.handle.getFile();
- zip.file(fileInfo.path, file);
- }
-
- // 更新进度
- progressBar.style.width = '50%';
- progressPercent.textContent = '50%';
- progressText.textContent = '打包完成,正在上传...';
-
- // 生成ZIP Blob
- const zipBlob = await zip.generateAsync({type: 'blob'});
-
- // 创建FormData对象用于上传
-
-
- const formData = new FormData();
- formData.append('file', zipBlob, `${directoryHandle.name}.zip`);
- formData.append('folderName', directoryHandle.name);
- // 上传到后端接口
- const response = await fetch('/upload_md_zip', {
- method: 'POST',
- body: formData
- });
-
- if (!response.ok) {
- throw new Error(`上传失败,状态码: ${response.status}`);
- }
-
- // 更新进度
- progressBar.style.width = '80%';
- progressPercent.textContent = '80%';
- progressText.textContent = '上传完成,正在处理文件...';
-
- const result = await response.json();
-
- // 更新进度
- progressBar.style.width = '100%';
- progressPercent.textContent = '100%';
- progressText.textContent = '处理完成!';
-
- // 重置按钮状态
- resetUploadButton(button, progressSection);
-
- if (result.success) {
- // 检查是否有处理的MD文件
- if (result.processed_files && result.processed_files.length > 0) {
- // 获取第一个MD文件的内容并加载到编辑器
- await loadFirstMdFileToEditor(result.extract_url, result.processed_files[0]);
- alert(`上传成功!已处理 ${result.processed_files.length} 个MD文件,第一个文件已加载到编辑器。`);
- } else if (result.md_file) {
- // 兼容旧版本返回格式
- await loadMdFileToEditor(result.extract_url, result.md_file);
- alert('上传成功!MD文件已加载到编辑器。');
- } else {
- alert('上传成功,但未找到可处理的MD文件。');
- }
- } else {
- throw new Error(result.message || '上传处理失败');
- }
-
- } catch (error) {
- console.error('错误:', error);
- alert('操作失败: ' + error.message);
- const button = document.getElementById('packButton');
- const progressSection = document.getElementById('progressSection');
- resetUploadButton(button, progressSection);
- }
- }
-
- // 重置上传按钮状态
- function resetUploadButton(button, progressSection) {
- if (button) {
- button.innerHTML = '<i class="fas fa-folder-open mr-2"></i>选择文件夹并打包上传';
- button.disabled = false;
- }
- if (progressSection) {
- // 延迟隐藏进度条,让用户看到完成状态
- setTimeout(() => {
- progressSection.classList.add('hidden');
- }, 2000);
- }
- }
-
- // 加载第一个MD文件到编辑器
- async function loadFirstMdFileToEditor(extractUrl, fileName) {
- try {
- // 构建MD文件的访问URL
- const mdFileUrl = `${extractUrl}/${fileName.path || fileName}`;
-
- // 获取MD文件内容
- const response = await fetch(mdFileUrl);
- if (!response.ok) {
- throw new Error('无法获取MD文件内容');
- }
-
- const mdContent = await response.text();
-
- // 设置到Vditor编辑器中
- if (vditorInstance) {
- vditorInstance.setValue(mdContent);
- currentMdContent = mdContent;
- } else {
- console.error('Vditor实例未找到');
- }
- } catch (error) {
- console.error('加载MD文件到编辑器失败:', error);
- throw new Error('MD文件加载失败: ' + error.message);
- }
- }
-
- // 兼容旧版本MD文件加载
- async function loadMdFileToEditor(extractUrl, mdFileInfo) {
- if (typeof mdFileInfo === 'string') {
- // 如果是文件名字符串
- await loadFirstMdFileToEditor(extractUrl, mdFileInfo);
- } else if (mdFileInfo && mdFileInfo.relative_path) {
- // 如果是文件信息对象
- await loadFirstMdFileToEditor(extractUrl, mdFileInfo.relative_path);
- }
- }
-
- // 递归获取文件夹中的所有文件
- async function getAllFiles(directoryHandle, path = '') {
- const files = [];
-
- for await (const entry of directoryHandle.values()) {
- const currentPath = path ? `${path}/${entry.name}` : entry.name;
-
- if (entry.kind === 'directory') {
- const subFiles = await getAllFiles(entry, currentPath);
- files.push(...subFiles);
- } else {
- files.push({
- handle: entry,
- path: currentPath
- });
- }
- }
-
- return files;
- }
- </script>
- </body>
- </html>
|