a_knowledgenote_edit.html 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>知识笔记管理系统</title>
  7. <!-- Bootstrap CSS -->
  8. <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
  9. <!-- Vditor CSS -->
  10. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vditor/dist/index.css">
  11. <!-- Font Awesome -->
  12. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  13. <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
  14. integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
  15. crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  16. <style>
  17. .hidden {
  18. display: none;
  19. }
  20. .editor-shadow {
  21. box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
  22. border-radius: 8px;
  23. padding: 20px;
  24. margin-bottom: 20px;
  25. }
  26. .progress-section {
  27. background-color: #f8f9fa;
  28. border-radius: 8px;
  29. padding: 15px;
  30. margin: 15px 0;
  31. }
  32. .upload-btn {
  33. background-color: #0d6efd;
  34. color: white;
  35. border: none;
  36. padding: 10px 20px;
  37. border-radius: 5px;
  38. cursor: pointer;
  39. transition: background-color 0.3s;
  40. }
  41. .upload-btn:hover {
  42. background-color: #0b5ed7;
  43. }
  44. .upload-btn:disabled {
  45. background-color: #6c757d;
  46. cursor: not-allowed;
  47. }
  48. .file-preview {
  49. max-height: 150px;
  50. max-width: 100%;
  51. margin-top: 10px;
  52. border: 1px solid #dee2e6;
  53. border-radius: 5px;
  54. }
  55. .form-section {
  56. margin-bottom: 25px;
  57. padding: 20px;
  58. border: 1px solid #e9ecef;
  59. border-radius: 8px;
  60. }
  61. .section-title {
  62. font-size: 1.2rem;
  63. font-weight: 600;
  64. margin-bottom: 15px;
  65. color: #495057;
  66. border-bottom: 1px solid #e9ecef;
  67. padding-bottom: 10px;
  68. }
  69. </style>
  70. </head>
  71. <body>
  72. <div class="container-fluid">
  73. <div class="row">
  74. <!-- 侧边导航 -->
  75. <nav class="col-md-2 d-md-block bg-light sidebar">
  76. <div class="sidebar-sticky pt-3">
  77. <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
  78. <span>知识管理</span>
  79. </h6>
  80. <ul class="nav flex-column">
  81. <li class="nav-item">
  82. <a class="nav-link active" href="/knowledgenotemanage">
  83. <i class="fas fa-book mr-2"></i> 笔记管理
  84. </a>
  85. </li>
  86. <li class="nav-item">
  87. <a class="nav-link" href="/newknowledgenote">
  88. <i class="fas fa-plus-circle mr-2"></i> 添加笔记
  89. </a>
  90. </li>
  91. </ul>
  92. </div>
  93. </nav>
  94. <!-- 主内容区域 -->
  95. <main role="main" class="col-md-10 ml-sm-auto px-4">
  96. <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
  97. <h1 class="h2">
  98. {% if status=='add' %}
  99. 添加知识笔记
  100. {% elif status=='edit' %}
  101. 编辑知识笔记
  102. {% endif %}
  103. </h1>
  104. </div>
  105. <!-- 消息提示 -->
  106. {% with messages = get_flashed_messages() %}
  107. {% if messages %}
  108. <div class="alert-container">
  109. {% for message in messages %}
  110. <div class="alert alert-info alert-dismissible fade show" role="alert">
  111. {{ message }}
  112. <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
  113. </div>
  114. {% endfor %}
  115. </div>
  116. {% endif %}
  117. {% endwith %}
  118. <!-- 笔记编辑表单 -->
  119. <form action="/editknowledgenote.do" method="post" enctype="multipart/form-data" onsubmit="return prepareFormSubmission()">
  120. <input type="hidden" name="lbl_status" value="{{ status }}"/>
  121. <!-- 基本信息部分 -->
  122. <div class="form-section">
  123. <div class="section-title">基本信息</div>
  124. <div class="row">
  125. <div class="col-md-6">
  126. <div class="mb-3">
  127. <label class="form-label">所属知识专题</label>
  128. {% if status=='add' %}
  129. <select name="txt_KTID" class="form-control" required>
  130. <option value="">请选择知识专题</option>
  131. {% for row1 in knowledgetopic %}
  132. <option value="{{row1[0]}}">{{row1[2]}}</option>
  133. {% endfor %}
  134. </select>
  135. {% elif status=='edit' and knowledgenote %}
  136. <input type="hidden" name="lbl_KNID" value="{{ knowledgenote[0] }}"/>
  137. <select name="txt_KTID" class="form-control" required>
  138. {% for row in knowledgetopic %}
  139. <option value="{{row[0]}}" {% if knowledgenote[1] == row[0] %}selected{% endif %}>{{row[2]}}</option>
  140. {% endfor %}
  141. </select>
  142. {% endif %}
  143. </div>
  144. </div>
  145. <div class="col-md-6">
  146. <div class="mb-3">
  147. <label class="form-label">笔记标题</label>
  148. <input name="txt_KNTitle" type="text" class="form-control" placeholder="填写笔记标题"
  149. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[2]}}" {% endif %} required>
  150. </div>
  151. </div>
  152. </div>
  153. <div class="row">
  154. <div class="col-md-6">
  155. <div class="mb-3">
  156. <label class="form-label">原创/转载</label>
  157. <div class="form-check form-check-inline">
  158. <input class="form-check-input" name="txt_IsOriginal" type="radio" value="1" id="original"
  159. {% if status=='add' %}checked{% endif %}
  160. {% if status=='edit' and knowledgenote and knowledgenote[3]==1 %}checked{% endif %}>
  161. <label class="form-check-label" for="original">原创</label>
  162. </div>
  163. <div class="form-check form-check-inline">
  164. <input class="form-check-input" name="txt_IsOriginal" type="radio" value="2" id="repost"
  165. {% if status=='edit' and knowledgenote and knowledgenote[3]==2 %}checked{% endif %}>
  166. <label class="form-check-label" for="repost">转载</label>
  167. </div>
  168. </div>
  169. <div class="mb-3">
  170. <label class="form-label">来源网站</label>
  171. <input name="txt_Source" type="text" class="form-control" placeholder="填写转载来源网站名称"
  172. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[4]}}" {% endif %}>
  173. </div>
  174. <div class="mb-3">
  175. <label class="form-label">网站链接</label>
  176. <input name="txt_OrigLink" type="text" class="form-control" placeholder="填写转载来源链接"
  177. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[5]}}" {% endif %}>
  178. </div>
  179. </div>
  180. <div class="col-md-6">
  181. <div class="mb-3">
  182. <label class="form-label">封面图片</label>
  183. <input id="file_proimg" name="file_proimg" type="file" class="form-control" accept="image/*">
  184. <input id="txt_KNImage" name="txt_KNImage" type="hidden"
  185. {% if status =='edit' and knowledgenote %} value="{{knowledgenote[6]}}" {% else %} value="" {% endif %}/>
  186. <div id="imagePreview" class="mt-2">
  187. {% if status =='edit' and knowledgenote and knowledgenote[6] %}
  188. <img src="/{{knowledgenote[6]}}" class="file-preview">
  189. {% endif %}
  190. </div>
  191. </div>
  192. </div>
  193. </div>
  194. <div class="row">
  195. <div class="col-md-6">
  196. <div class="mb-3">
  197. <label class="form-label">作者</label>
  198. <input name="txt_CoAuthors" type="text" class="form-control" placeholder="填写作者"
  199. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[7]}}" {% endif %}>
  200. </div>
  201. </div>
  202. <div class="col-md-6">
  203. <div class="mb-3">
  204. <label class="form-label">是否显示</label>
  205. <select name="txt_KNIsShow" class="form-control">
  206. <option value="1" {% if status=='add' %}selected{% endif %}
  207. {% if status=='edit' and knowledgenote and knowledgenote[15]==1 %}selected{% endif %}>显示</option>
  208. <option value="0" {% if status=='edit' and knowledgenote and knowledgenote[15]==0 %}selected{% endif %}>不显示</option>
  209. </select>
  210. </div>
  211. </div>
  212. </div>
  213. <div class="mb-3">
  214. <label class="form-label">摘要</label>
  215. <textarea name="txt_KNAbstract" class="form-control" rows="3" placeholder="填写笔记摘要">{% if status=='edit' and knowledgenote %}{{knowledgenote[8]}}{% endif %}</textarea>
  216. </div>
  217. </div>
  218. <!-- 内容编辑部分 -->
  219. <div class="form-section">
  220. <div class="section-title">笔记内容</div>
  221. <!-- MD文件上传区域 -->
  222. <div class="mb-3">
  223. <button type="button" id="packButton" class="upload-btn" onclick="uploadMdFile()">
  224. <i class="fas fa-folder-open mr-2"></i>选择文件夹并打包上传
  225. </button>
  226. <!-- 进度显示区域 -->
  227. <div id="progressSection" class="progress-section hidden">
  228. <div class="d-flex justify-content-between align-items-center mb-2">
  229. <h5 class="mb-0">处理进度</h5>
  230. <span id="progressPercent" class="fw-bold">0%</span>
  231. </div>
  232. <div class="progress mb-2">
  233. <div id="progressBar" class="progress-bar" role="progressbar" style="width: 0%"></div>
  234. </div>
  235. <p id="progressText" class="mb-0 text-muted">准备处理文件...</p>
  236. </div>
  237. </div>
  238. <!-- Vditor编辑器 -->
  239. <input type="hidden" id="txt_KNcontent" name="txt_KNcontent"/>
  240. <div id="vditor"></div>
  241. </div>
  242. <!-- 附件部分 -->
  243. <div class="form-section">
  244. <div class="section-title">附件信息</div>
  245. <div class="row">
  246. <div class="col-md-6">
  247. <div class="mb-3">
  248. <label class="form-label">视频文件附件</label>
  249. <input name="txt_VideoLink" type="text" class="form-control" placeholder="视频文件路径/static/uploads/video_notes/"
  250. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[12]}}" {% endif %}>
  251. <input id="file_video" name="file_video" type="file" class="form-control mt-2" accept="video/*">
  252. </div>
  253. </div>
  254. <div class="col-md-6">
  255. <div class="mb-3">
  256. <label class="form-label">文件附件</label>
  257. <input name="txt_FileLink" type="text" class="form-control" placeholder="文件路径"
  258. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[10]}}" {% endif %}>
  259. </div>
  260. </div>
  261. </div>
  262. <div class="row">
  263. <div class="col-md-6">
  264. <div class="mb-3">
  265. <label class="form-label">PPT文件附件</label>
  266. <input name="txt_PPTLink" type="text" class="form-control" placeholder="PPT文件路径"
  267. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[11]}}" {% endif %}>
  268. </div>
  269. </div>
  270. <div class="col-md-6">
  271. <div class="mb-3">
  272. <label class="form-label">代码文件附件</label>
  273. <input name="txt_CodeLink" type="text" class="form-control" placeholder="代码文件路径"
  274. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[13]}}" {% endif %}>
  275. </div>
  276. </div>
  277. </div>
  278. <div class="row">
  279. <div class="col-md-6">
  280. <div class="mb-3">
  281. <label class="form-label">数据文件附件</label>
  282. <input name="txt_DataLink" type="text" class="form-control" placeholder="数据文件路径"
  283. {% if status=='edit' and knowledgenote %} value="{{knowledgenote[14]}}" {% endif %}>
  284. </div>
  285. </div>
  286. </div>
  287. </div>
  288. <!-- 操作按钮 -->
  289. <div class="d-flex justify-content-end gap-2 mt-4">
  290. <button type="submit" class="btn btn-primary">
  291. <i class="fas fa-save mr-2"></i>保存笔记
  292. </button>
  293. <a href="/knowledgenotemanage" class="btn btn-secondary">
  294. <i class="fas fa-arrow-left mr-2"></i>返回列表
  295. </a>
  296. </div>
  297. </form>
  298. </main>
  299. </div>
  300. </div>
  301. <!-- Bootstrap JS -->
  302. <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
  303. <!-- Vditor JS -->
  304. <script src="https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js"></script>
  305. <!-- JSZip -->
  306. <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
  307. <script>
  308. $(function(){
  309. $("#file_proimg").change(function(){
  310. //alert($(this).val());
  311. $("#txt_RTImage").val($(this).val());
  312. var objUrl=getObjectURL(this.files[0]);
  313. //console.log("objUrl="+objUrl);
  314. if(objUrl){
  315. $("#img_proimg").attr("src",objUrl);
  316. $("#file_proimg").attr("src",objUrl);
  317. }
  318. })
  319. //建立一個可存取到該file的url
  320. function getObjectURL(file) {
  321. var url = null ;
  322. if (window.createObjectURL!=undefined) {
  323. url = window.createObjectURL(file) ;
  324. } else if (window.URL!=undefined) {
  325. url = window.URL.createObjectURL(file) ;
  326. } else if (window.webkitURL!=undefined) {
  327. url = window.webkitURL.createObjectURL(file) ;
  328. }
  329. return url ;
  330. }
  331. })
  332. //选择视频
  333. $(function(){
  334. $("#file_video").change(function(){
  335. //alert($(this).val());
  336. $("#txt_VideoLink").val($(this).val());
  337. var objUrl=getObjectURL(this.files[0]);
  338. //console.log("objUrl="+objUrl);
  339. if(objUrl){
  340. $("#file_video").attr("src",objUrl);
  341. }
  342. })
  343. //建立一個可存取到該file的url
  344. function getObjectURL(file) {
  345. var url = null ;
  346. if (window.createObjectURL!=undefined) {
  347. url = window.createObjectURL(file) ;
  348. } else if (window.URL!=undefined) {
  349. url = window.URL.createObjectURL(file) ;
  350. } else if (window.webkitURL!=undefined) {
  351. url = window.webkitURL.createObjectURL(file) ;
  352. }
  353. return url ;
  354. }
  355. })
  356. function getV() {
  357. $("#txt_KNcontent").val(JSON.stringify(vditor.getValue()))
  358. }
  359. // 全局变量
  360. let vditorInstance;
  361. let currentMdContent = '';
  362. // 页面加载完成后初始化
  363. document.addEventListener('DOMContentLoaded', function() {
  364. // 初始化Vditor编辑器
  365. initVditor();
  366. // 绑定文件预览事件
  367. bindFilePreviewEvents();
  368. });
  369. // 初始化Vditor编辑器
  370. function initVditor() {
  371. vditorInstance = new Vditor('vditor', {
  372. height: 600,
  373. cache: {
  374. enable: false
  375. },
  376. value: '{% if status=="edit" and knowledgenote and knowledgenote[9] %}{{ knowledgenote[9] | tojson }}{% else %}{% endif %}',
  377. mode: 'wysiwyg',
  378. theme: 'classic',
  379. icon: 'material',
  380. upload: {
  381. url: '/vditor_web/uploads',
  382. linkToImgUrl: '/static/uploads/img_comms/',
  383. accept: '.jpg,.png,.gif,.jpeg',
  384. filename: function(name) {
  385. return name.replace(/\?|\\|\/|:|\||<|>|\*|\[|\]|\s+/g, '-');
  386. },
  387. },
  388. after: function() {
  389. console.log('Vditor编辑器初始化完成');
  390. }
  391. });
  392. }
  393. // 绑定文件预览事件
  394. function bindFilePreviewEvents() {
  395. // 封面图片预览
  396. const fileProimg = document.getElementById('file_proimg');
  397. if (fileProimg) {
  398. fileProimg.addEventListener('change', function(e) {
  399. const file = e.target.files[0];
  400. if (file) {
  401. const reader = new FileReader();
  402. reader.onload = function(e) {
  403. const preview = document.getElementById('imagePreview');
  404. preview.innerHTML = `<img src="${e.target.result}" class="file-preview">`;
  405. };
  406. reader.readAsDataURL(file);
  407. }
  408. });
  409. }
  410. }
  411. // 准备表单提交
  412. function prepareFormSubmission() {
  413. if (vditorInstance) {
  414. const content = vditorInstance.getValue();
  415. document.getElementById('txt_KNcontent').value = JSON.stringify(content);
  416. }
  417. return true;
  418. }
  419. // 上传MD文件函数
  420. async function uploadMdFile() {
  421. try {
  422. // 检查浏览器是否支持文件夹选择
  423. if (!window.showDirectoryPicker) {
  424. alert('您的浏览器不支持文件夹选择功能,请使用Chrome或Edge等现代浏览器');
  425. return;
  426. }
  427. // 选择文件夹
  428. const directoryHandle = await window.showDirectoryPicker();
  429. const button = document.getElementById('packButton');
  430. if (button) {
  431. button.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>正在打包中...';
  432. button.disabled = true;
  433. }
  434. // 显示进度区域
  435. const progressSection = document.getElementById('progressSection');
  436. const progressBar = document.getElementById('progressBar');
  437. const progressPercent = document.getElementById('progressPercent');
  438. const progressText = document.getElementById('progressText');
  439. progressSection.classList.remove('hidden');
  440. progressBar.style.width = '10%';
  441. progressPercent.textContent = '10%';
  442. progressText.textContent = '正在读取文件夹内容...';
  443. // 获取所有文件
  444. const files = await getAllFiles(directoryHandle);
  445. if (files.length === 0) {
  446. alert('选择的文件夹为空');
  447. resetUploadButton(button, progressSection);
  448. return;
  449. }
  450. // 更新进度
  451. progressBar.style.width = '30%';
  452. progressPercent.textContent = '30%';
  453. progressText.textContent = `找到 ${files.length} 个文件,正在打包...`;
  454. // 创建ZIP文件
  455. const zip = new JSZip();
  456. for (const fileInfo of files) {
  457. const file = await fileInfo.handle.getFile();
  458. zip.file(fileInfo.path, file);
  459. }
  460. // 更新进度
  461. progressBar.style.width = '50%';
  462. progressPercent.textContent = '50%';
  463. progressText.textContent = '打包完成,正在上传...';
  464. // 生成ZIP Blob
  465. const zipBlob = await zip.generateAsync({type: 'blob'});
  466. // 创建FormData对象用于上传
  467. const formData = new FormData();
  468. formData.append('file', zipBlob, `${directoryHandle.name}.zip`);
  469. formData.append('folderName', directoryHandle.name);
  470. // 上传到后端接口
  471. const response = await fetch('/upload_md_zip', {
  472. method: 'POST',
  473. body: formData
  474. });
  475. if (!response.ok) {
  476. throw new Error(`上传失败,状态码: ${response.status}`);
  477. }
  478. // 更新进度
  479. progressBar.style.width = '80%';
  480. progressPercent.textContent = '80%';
  481. progressText.textContent = '上传完成,正在处理文件...';
  482. const result = await response.json();
  483. // 更新进度
  484. progressBar.style.width = '100%';
  485. progressPercent.textContent = '100%';
  486. progressText.textContent = '处理完成!';
  487. // 重置按钮状态
  488. resetUploadButton(button, progressSection);
  489. if (result.success) {
  490. // 检查是否有处理的MD文件
  491. if (result.processed_files && result.processed_files.length > 0) {
  492. // 获取第一个MD文件的内容并加载到编辑器
  493. await loadFirstMdFileToEditor(result.extract_url, result.processed_files[0]);
  494. alert(`上传成功!已处理 ${result.processed_files.length} 个MD文件,第一个文件已加载到编辑器。`);
  495. } else if (result.md_file) {
  496. // 兼容旧版本返回格式
  497. await loadMdFileToEditor(result.extract_url, result.md_file);
  498. alert('上传成功!MD文件已加载到编辑器。');
  499. } else {
  500. alert('上传成功,但未找到可处理的MD文件。');
  501. }
  502. } else {
  503. throw new Error(result.message || '上传处理失败');
  504. }
  505. } catch (error) {
  506. console.error('错误:', error);
  507. alert('操作失败: ' + error.message);
  508. const button = document.getElementById('packButton');
  509. const progressSection = document.getElementById('progressSection');
  510. resetUploadButton(button, progressSection);
  511. }
  512. }
  513. // 重置上传按钮状态
  514. function resetUploadButton(button, progressSection) {
  515. if (button) {
  516. button.innerHTML = '<i class="fas fa-folder-open mr-2"></i>选择文件夹并打包上传';
  517. button.disabled = false;
  518. }
  519. if (progressSection) {
  520. // 延迟隐藏进度条,让用户看到完成状态
  521. setTimeout(() => {
  522. progressSection.classList.add('hidden');
  523. }, 2000);
  524. }
  525. }
  526. // 加载第一个MD文件到编辑器
  527. async function loadFirstMdFileToEditor(extractUrl, fileName) {
  528. try {
  529. // 构建MD文件的访问URL
  530. const mdFileUrl = `${extractUrl}/${fileName.path || fileName}`;
  531. // 获取MD文件内容
  532. const response = await fetch(mdFileUrl);
  533. if (!response.ok) {
  534. throw new Error('无法获取MD文件内容');
  535. }
  536. const mdContent = await response.text();
  537. // 设置到Vditor编辑器中
  538. if (vditorInstance) {
  539. vditorInstance.setValue(mdContent);
  540. currentMdContent = mdContent;
  541. } else {
  542. console.error('Vditor实例未找到');
  543. }
  544. } catch (error) {
  545. console.error('加载MD文件到编辑器失败:', error);
  546. throw new Error('MD文件加载失败: ' + error.message);
  547. }
  548. }
  549. // 兼容旧版本MD文件加载
  550. async function loadMdFileToEditor(extractUrl, mdFileInfo) {
  551. if (typeof mdFileInfo === 'string') {
  552. // 如果是文件名字符串
  553. await loadFirstMdFileToEditor(extractUrl, mdFileInfo);
  554. } else if (mdFileInfo && mdFileInfo.relative_path) {
  555. // 如果是文件信息对象
  556. await loadFirstMdFileToEditor(extractUrl, mdFileInfo.relative_path);
  557. }
  558. }
  559. // 递归获取文件夹中的所有文件
  560. async function getAllFiles(directoryHandle, path = '') {
  561. const files = [];
  562. for await (const entry of directoryHandle.values()) {
  563. const currentPath = path ? `${path}/${entry.name}` : entry.name;
  564. if (entry.kind === 'directory') {
  565. const subFiles = await getAllFiles(entry, currentPath);
  566. files.push(...subFiles);
  567. } else {
  568. files.push({
  569. handle: entry,
  570. path: currentPath
  571. });
  572. }
  573. }
  574. return files;
  575. }
  576. </script>
  577. </body>
  578. </html>