Переглянути джерело

导入书签数据,并解析

john 9 місяців тому
батько
коміт
f714c7be2b

+ 4 - 0
.prettierrc

@@ -0,0 +1,4 @@
+{
+  "tabWidth": 2,
+  "useTabs": false
+}

+ 11 - 0
.prettierrc.js

@@ -0,0 +1,11 @@
+// .prettierrc.js
+module.exports = {
+  semi: true,
+  trailingComma: 'es5',
+  singleQuote: true,
+  printWidth: 80,
+  tabWidth: 2,
+  endOfLine: 'lf',
+  bracketSpacing: true,
+  arrowParens: 'always',
+};

+ 7 - 1
docs/收藏夹管理.md

@@ -17,4 +17,10 @@
 - **验证和格式化**:使用HTML验证工具检查文件的结构是否正确,特别是标签的开闭合是否匹配。这是避免浏览器导入时出错的关键步骤。
 - **自动化处理**:可以编写脚本(如Python脚本)来自动化处理这些书签文件,例如批量修改链接、合并书签等。
 
-这类文件虽然结构简单,但正确的处理和维护对于保证数据的正确性和可用性非常重要。如果你有特定的需求或想要实现特定的功能,可以提供更多详细信息,我可以帮助你更精确地处理这类文件。
+这类文件虽然结构简单,但正确的处理和维护对于保证数据的正确性和可用性非常重要。如果你有特定的需求或想要实现特定的功能,可以提供更多详细信息,我可以帮助你更精确地处理这类文件。
+
+
+
+## 参考文档
+- [浏览器书签|收藏夹(bookmarks)结构格式分析](https://www.onekbit.com/ViewBlog/blog/BID20200210100286#google_vignette)
+- [Netscape 书签文件格式](https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa753582(v=vs.85))

+ 10 - 2
src/Router.tsx

@@ -24,8 +24,8 @@ import FilesManage from "@/pages/DuplicateFile/FilesManage";
 import ChatPage from "@/pages/Chat/Chat";
 
 /* 收藏夹管理 */
-import BookmarksIndex from "@/pages/Bookmarks/List";
-import BookmarksList from "@/pages/Bookmarks/List";
+import BookmarksListPage from "@/pages/Bookmarks/List";
+import BookmarksManagePage from "@/pages/Bookmarks/Manage";
 import Chat from "@/pages/Chat/Chat";
 /* export default function Router() {
   return (
@@ -114,6 +114,14 @@ const router = createBrowserRouter(
         {
           path: "chat",
           element: <ChatPage />,
+        },
+        {
+          path: "bookmarksList",
+          element: <BookmarksListPage />,
+        },
+        {
+          path: "bookmarksManage",
+          element: <BookmarksManagePage />,
         }
         // {
         //   path: "duplicate-file",

+ 7 - 0
src/components/Layout/breadcrumbManage.tsx

@@ -77,4 +77,11 @@ export default ({
       calculateFn({ title: "chat", isCallBack: false }),
     ]);
   }
+
+  // 书签 的面包屑配置
+  if (/^\/bookmarks/.test(location.pathname)) {
+    setPlaceholder([
+      calculateFn({ title: "Bookmark管理器", isCallBack: false }),
+    ]);
+  }
 };

+ 16 - 7
src/components/Menu/Menu.tsx

@@ -13,12 +13,17 @@ export default function Menu() {
   }, []);
 
   async function initMenu() {
-    // console.log(141414, location);
-
-    // const config = LogicalSize;
-    // console.log({ LogicalSize: new LogicalSize() });
-    // console.log(window);
-    // setHeight(await window);
+    function getActive(arr) {
+      arr.forEach(item => {
+        if(item.hasOwnProperty('path') && (item.path.includes(location.pathname) || item.path === location.pathname ) && !active) {
+          setActive(item.label); 
+        }
+        if(item.hasOwnProperty('child') && !active) {
+          getActive(item.child)
+        }
+      })
+    }
+    getActive(menuConfig)
   }
   const menuConfig = [
     {
@@ -40,6 +45,10 @@ export default function Menu() {
           label: "chat",
           path: "/chat",
         },
+        {
+          label: "Bookmark管理器",
+          path: "/bookmarksList",
+        },
       ],
     },
     {
@@ -65,7 +74,7 @@ export default function Menu() {
   }
   return (
     <div className={clsx(styles.box, styles.parent)}>
-      <div className={styles.logoBox}>占位符:</div>
+      <div className={styles.logoBox}>Logo:</div>
       {menuConfig.map((item) => (
         <div className={styles.menuBox} key={item.label}>
           <div className={styles.label}>{item.label}</div>

+ 15 - 0
src/databases/createTableSql.ts

@@ -43,4 +43,19 @@ export const createSql = {
         idsNum INTEGER,
         UNIQUE (hash)
     );`,
+  bookmarks: `CREATE TABLE IF NOT EXISTS bookmarks (
+        id INTEGER PRIMARY KEY AUTOINCREMENT,
+        create_time TIMESTAMP,
+        ADD_DATE TIMESTAMP,
+        LAST_MODIFIED TIMESTAMP,
+        label TEXT,
+        type TEXT,
+        PERSONAL_TOOLBAR_FOLDER  TEXT,
+        ICON TEXT,
+        name TEXT,
+        HREF TEXT,
+        hash TEXT,
+        level INTEGER,
+        parent_id INTEGER,
+    );`,
 };

+ 11 - 0
src/pages/Bookmarks/List.module.less

@@ -0,0 +1,11 @@
+.ListPage {
+  padding: 24px;
+  .pageTitle {
+    font-size: 40px;
+    margin-right: 12px;
+  }
+  .stepsBox {
+    width: 80vw;
+    margin: 10vh auto 100px;
+  }
+}

+ 110 - 0
src/pages/Bookmarks/List.tsx

@@ -1,3 +1,113 @@
+import { useRoutes } from "react-router";
+import { useNavigate } from "react-router-dom";
+import { open as dialogOpen } from "@tauri-apps/api/dialog";
+import { readTextFile, writeTextFile } from "@tauri-apps/api/fs";
+
+import styles from "./List.module.less";
+
 export default function List() {
+  let navigate = useNavigate();
+  /*
+
+
+	const bookmarkHtml = await readTextFile("/Users/sysadmin/code/rust_project/tauri-app/docs/favorites_2024_6_28_kerrty.html");
+	    // const bookmarkHtml = await readTextFile("/Users/sysadmin/code/rust_project/tauri-app/docs/favorites_2024_6_28.html");
+
+
+	    */
+  async function importXMLFile() {
+    const bookmarkHtml = await readTextFile(
+      "/Users/sysadmin/code/rust_project/tauri-app/docs/favorites_2024_6_28_kerrty.html",
+    );
+    const bookmarkHtmlArray = bookmarkHtml.split(/\n/);
+    const spaceReg = /^\s+/;
+    let isStart = false;
+    let spaceLen = 0;
+    let parentArray: any = [];
+    let currentParent = null;
+    const tree = [] as any;
+
+    bookmarkHtmlArray.forEach((lineStr) => {
+      if (!isStart) {
+        isStart = lineStr.indexOf("<DL") > -1; // 检查是否开始
+      }
+      if (isStart && lineStr) {
+        let spaceStr = "";
+        let isDir = lineStr.indexOf("<DT><H3") > -1;
+        let isBookmark = lineStr.indexOf("<DT><A") > -1;
+        const docObj = {} as any;
+
+        // 判断缩进
+        if (spaceReg.test(lineStr)) {
+            const lineStrMatch = lineStr.match(spaceReg);
+            if(lineStrMatch) {
+                spaceStr = lineStrMatch[0];
+            }
+        }
+
+        // 设置缩进的基准长度
+        if (spaceStr.length && spaceLen === 0) {
+          spaceLen = spaceStr.length;
+        }
+
+        // 计算书签层级
+        docObj.level = spaceStr.length / spaceLen || 0;
+
+        // 使用 DOMParser 解析 HTML 字符串
+        let parser = new DOMParser();
+        let doc = parser.parseFromString(lineStr, "text/html");
+        let nodeDom = null as any;
+        if (isBookmark || isDir) {
+          nodeDom = doc.getElementsByTagName("h3")[0];
+          docObj.type = isBookmark ? "bookmark" : "dir";
+          nodeDom = doc.getElementsByTagName(isBookmark ? "a" : "h3")[0];
+
+          docObj.label = nodeDom.innerText;
+          nodeDom.getAttributeNames().forEach((key: string) => {
+            docObj[key] = nodeDom.getAttribute(key);
+          });
+
+          // 层级为 1 时,意味着是顶层节点
+          if (docObj.level === 1) {
+            parentArray = [{ level: docObj.level, label: docObj.label }];
+            docObj.parentLabel = '';
+            docObj.parentLevel = '';
+          } else {
+            // 管理父子层级关系
+            while (parentArray.length > docObj.level) {
+              parentArray.pop(); // 退回到正确的层级
+            }
+
+            // 设置当前父节点
+            currentParent = [...parentArray].pop();
+            if (currentParent.level === docObj.level) {
+              currentParent = parentArray.pop(2);
+            }
+            docObj.parentLabel = currentParent.label;
+            docObj.parentLevel = currentParent.level;
 
+            // 如果当前是目录,入栈
+            if (isDir) {
+              parentArray.push({ level: docObj.level, label: docObj.label });
+            }
+          }
+          // tree.push(docObj);
+          console.log(docObj);
+        }
+      }
+    });
+    /* writeTextFile('Users/sysadmin/code/rust_project/tauri-app/docs/tree.text', (tree as any).toString('\n'), {
+            append: true
+        }) */
+    //    console.log(1000, tree);
+  }
+  async function exportXMLFile() {}
+  return (
+    <div className={styles.ListPage}>
+      {/*导入文件*/}
+      <div onClick={() => importXMLFile()}>导入</div>
+      <div onClick={() => exportXMLFile()}>导出</div>
+      {/*<div onClick={() => navigate('/bookmarksManage')}>to</div>*/}
+    </div>
+  );
 }

+ 11 - 0
src/pages/Bookmarks/Manage.module.less

@@ -0,0 +1,11 @@
+.ManagePage {
+  padding: 24px;
+  .pageTitle {
+    font-size: 40px;
+    margin-right: 12px;
+  }
+  .stepsBox {
+    width: 80vw;
+    margin: 10vh auto 100px;
+  }
+}

+ 10 - 0
src/pages/Bookmarks/Manage.tsx

@@ -0,0 +1,10 @@
+import { useRoutes } from "react-router";
+import { useNavigate } from "react-router-dom";
+import styles from "./Manage.module.less";
+
+export default function Manage() {
+	let navigate = useNavigate();
+	return (
+		<div className={styles.ManagePage}  onClick={() => navigate(-1)}>Manage</div>
+	)
+}

+ 7 - 0
src/plugins/bookmarks/bookmarks.ts

@@ -0,0 +1,7 @@
+export class BookMarks {
+  lineText: string;
+  level: number;
+  constructor(path: string) {
+    this.path = path;
+  }
+}

+ 1 - 0
src/plugins/bookmarks/index.ts

@@ -0,0 +1 @@
+export * from './bookmarks';