Bladeren bron

增加代码中的筛选条件

john 1 jaar geleden
bovenliggende
commit
0f48bf0399

+ 122 - 44
src-tauri/src/self_plugin/tauri_plugin_file/files.rs

@@ -1,13 +1,13 @@
 use std::fs;
-// use crypto::digest::Digest;
-// use crypto::sha2::Sha256;
 use ring::digest::{Context, Digest, SHA256};
-use serde::Serialize;
-use serde::Serializer;
-use std::path::Path;
-use std::path::PathBuf;
-// use tauri::api::path::resolve_path;
+use serde::{Serialize, Serializer, Deserialize};
+use std::io::{self, Read};
+use sha2::{Sha256, Digest as OtherDigest};  // 确保导入 `Digest`
+use hex;
 use tauri::command;
+use std::path::{Path, PathBuf};
+use std::result::Result as new_Result;
+// use std::result::Result;
 // use tauri::api::file::IntoInvokeHandler;
 
 #[derive(Debug, thiserror::Error)]
@@ -48,43 +48,81 @@ fn filter_other_directory(path: &str, directories: &[&str]) -> bool {
     true
 }
 
-fn read_files_in_directory(directory: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
-    if let Ok(entries) = fs::read_dir(directory) {
-        for entry in entries {
-            if let Ok(entry) = entry {
-                let path = entry.path();
-                if path.is_file()
-                    && filter_other_directory(
-                        path.display().to_string().as_str(),
-                        &[".obsidian", ".DS_Store"],
-                    )
-                {
-                    // 过滤文件
-                    // TODO 后续加上需要过滤的文件
-                    println!("59{}", path.display());
-                    files.push(path.clone());
-                } else if path.is_dir()
-                    && filter_other_directory(
-                        path.display().to_string().as_str(),
-                        &["node_modules", ".git", ".obsidian", ".DS_Store"],
-                    )
-                {
-                    // 过滤 目录
-                    // println!("{}", path.display());
-                    read_files_in_directory(&path, files)?;
-                }
-            }
-        }
-    }
-    Ok(())
+// fn read_files_in_directory(directory: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
+//     if let Ok(entries) = fs::read_dir(directory) {
+//         for entry in entries {
+//             if let Ok(entry) = entry {
+//                 let path = entry.path();
+//                 if path.is_file()
+//                     && filter_other_directory(
+//                         path.display().to_string().as_str(),
+//                         &[".obsidian", ".DS_Store"],
+//                     )
+//                 {
+//                     // 过滤文件
+//                     // TODO 后续加上需要过滤的文件
+//                     println!("59{}", path.display());
+//                     files.push(path.clone());
+//                 } else if path.is_dir()
+//                     && filter_other_directory(
+//                         path.display().to_string().as_str(),
+//                         &["node_modules", ".git", ".obsidian", ".DS_Store"],
+//                     )
+//                 {
+//                     // 过滤 目录
+//                     // println!("{}", path.display());
+//                     read_files_in_directory(&path, files)?;
+//                 }
+//             }
+//         }
+//     }
+//     Ok(())
+// }
+
+
+#[derive(Debug, Deserialize, Serialize)]
+pub enum FileSizeCategory {
+    Huge,       // 4GB+
+    VeryLarge,  // 1GB to 4GB-
+    Large,      // 128MB to 1GB-
+    Medium,     // 1MB to 128MB-
+    Small,      // 16KB to 1MB-
+    Tiny,       // 1B to 16KB-
+    Empty,      // Empty files or directories
 }
 
+
+
+#[derive(Debug, Deserialize, Serialize)]
+pub struct FileInfo {
+    pub path: Option<String>,
+    pub checkbox_all: Option<bool>,
+    pub add_type: Option<String>,
+    pub pass_type: Option<String>,
+    // pub checked_size_values: Option<Vec<String>>, // 假设值类型为 String,具体类型视情况调整
+    pub checked_size_values: Option<Vec<FileSizeCategory>>, // 使用正确的类型
+    pub checkbox_size_all: Option<bool>,
+    pub checked_type_values: Option<Vec<String>>, // 同上
+    pub time: Option<String>,
+    pub id: Option<u32>,
+    pub progress: Option<f32>,
+}
+
+
 #[command]
-pub fn get_all_directory(path: String) -> Result<Vec<PathBuf>> {
-    let directory = Path::new(&path);
+pub fn get_all_directory(file_info: FileInfo) -> Result<Vec<PathBuf>> {
     let mut files = Vec::new();
-    read_files_in_directory(directory, &mut files)?;
-    Ok(files)
+    if let Some(ref path) = file_info.path {
+        println!("Processing directory: {}", path);
+        let directory = Path::new(path);
+        // 确保 read_files_in_directory 能返回一个 Result<(), Error>
+        read_files_in_directory(directory, &mut files, &file_info.checked_size_values)?;
+        Ok(files)
+    } else {
+        // 当没有提供路径时返回错误
+        // Err(Error::new(std::io::ErrorKind::NotFound, "No path provided"))
+        Ok(files)
+    }
 }
 
 // #[command]
@@ -110,12 +148,52 @@ pub fn get_file_type_by_path(file_path: String) -> Result<String> {
     }
 }
 
+fn read_files_in_directory(dir: &Path, files: &mut Vec<PathBuf>, filters: &Option<Vec<FileSizeCategory>>) -> Result<()> {
+    if dir.is_dir() {
+        for entry in fs::read_dir(dir)? {
+            let entry = entry?;
+            let path = entry.path();
+            if path.is_dir() {
+                read_files_in_directory(&path, files, filters)?;
+            } else {
+                if let Ok(metadata) = fs::metadata(&path) {
+                    let size = metadata.len();
+                    if filters.is_none() || file_size_matches(size, filters.as_ref().unwrap()) {
+                        files.push(path);
+                    }
+                }
+            }
+        }
+    }
+    Ok(())
+}
+
+
+fn file_size_matches(size: u64, categories: &Vec<FileSizeCategory>) -> bool {
+    categories.iter().any(|category| match category {
+        FileSizeCategory::Huge => size >= 4294967296,
+        FileSizeCategory::VeryLarge => size >= 1073741824 && size < 4294967296,
+        FileSizeCategory::Large => size >= 134217728 && size < 1073741823,
+        FileSizeCategory::Medium => size >= 1048576 && size < 134217728,
+        FileSizeCategory::Small => size >= 16384 && size < 1048576,
+        FileSizeCategory::Tiny => size >= 1 && size < 16384,
+        FileSizeCategory::Empty => size == 0,
+    })
+}
+
 #[command]
 pub fn calculate_file_hash(file_path: String) -> Result<String> {
-    let file_bytes = fs::read(file_path).expect("无法读取文件");
-    let mut hasher = Context::new(&SHA256);
+    // 使用 `?` 代替 `.expect` 来优雅地处理错误
+    let file_bytes = fs::read(file_path)?;
+
+    // 初始化 SHA256 哈希上下文
+    let mut hasher = Sha256::new();
     hasher.update(&file_bytes);
-    let digest: Digest = hasher.finish();
-    let hash = hex::encode(digest.as_ref());
+
+    // 完成哈希计算
+    let result = hasher.finalize();
+
+    // 将结果转换为十六进制字符串
+    let hash = hex::encode(result);
     Ok(hash)
 }

+ 5 - 0
src/Router.tsx

@@ -9,6 +9,7 @@ import FileSort from "@/pages/FileSort/FileSort";
 import FileClear from "@/pages/FileClear/FileClear";
 import DuplicateFileIndex from "@/pages/DuplicateFile/Index";
 import DuplicateFile from "@/pages/DuplicateFile/DuplicateFile";
+import CalculateDuplicateFiles from "@/pages/DuplicateFile/CalculateDuplicateFiles";
 import DuplicateFileInfo from "@/pages/DuplicateFile/FileInfo";
 /* export default function Router() {
   return (
@@ -54,6 +55,10 @@ const router = createBrowserRouter([
           {
             path: "info/:fileId",
             element: <DuplicateFileInfo />,
+          },
+          {
+            path: "calculate/:fileId",
+            element: <CalculateDuplicateFiles />,
           }
         ]
       },

+ 1 - 0
src/components/Layout/Layout.module.less

@@ -4,6 +4,7 @@
   height: 100vh;
   .menuBox {
     flex: 0 0 200px;
+    border-inline-end: 1px solid rgba(5, 5, 5, 0.06);
   }
   .content {
     background-color: white;

+ 11 - 0
src/pages/DuplicateFile/CalculateDuplicateFiles.module.less

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

+ 109 - 0
src/pages/DuplicateFile/CalculateDuplicateFiles.tsx

@@ -0,0 +1,109 @@
+import {
+    delSelectedFileHistory,
+    get_all_history, get_info_by_id,
+    get_info_by_path,
+    insertSeletedFileHistory,
+    updateSelectedFileHistory
+} from "@/services";
+import {useEffect, useState} from "react";
+import {useParams} from "react-router-dom";
+import {FileInfoType, stepsStatusType} from "@/types/files";
+import {message} from "@tauri-apps/api/dialog";
+import styles from './CalculateDuplicateFiles.module.less'
+import File from "@/plugins/tauri-plugin-file/file";
+import {Button, Col, Row, Steps} from "antd";
+import {LoadingOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
+import { readDir, BaseDirectory } from '@tauri-apps/api/fs';
+
+export default function CalculateDuplicateFiles() {
+    let {fileId} = useParams();
+    const [fileInfo, setFileInfo] = useState<FileInfoType>({})
+    const [current, setCurrent] = useState(0);
+    const [percent, setPercent] = useState(0);
+    const [stepsStatus, setStepsStatus] = useState<stepsStatusType>({
+        scanDir: 'wait',
+        fileOptions: 'wait',
+        duplicateFiles: 'wait',
+        done: 'wait',
+    })
+    useEffect(() => {
+        pageInit()
+    }, []);
+
+    async function pageInit() {
+        if (fileId) {
+            const [data, errorMsg] = await get_info_by_id(Number.parseInt(fileId));
+            if (data && typeof data === 'object') {
+                setFileInfo(data)
+            } else {
+                await message(errorMsg, {title: '查询失败', type: 'error'});
+            }
+        }
+    }
+
+    async function getFiles() {
+        if (fileInfo.path) {
+            console.log(4545)
+            setStepsStatus({
+                ...stepsStatus,
+                fileOptions: 'process'
+            })
+            //
+            // const files = await File.getAllList(fileInfo.path);
+            // console.log(34, files)
+
+            // /Users/honghaitao/Downloads/PDF Expert Installer.app
+
+            // const hash = await File.getHash('/Users/honghaitao/Downloads/PDF Expert Installer.app')
+            // console.log(39, hash)
+        }
+    }
+    async function scanDirAll() {
+        if(fileInfo.path) {
+            console.log(626262, fileInfo)
+            const files = await File.getAllList(fileInfo);
+            console.log(636363, files)
+        }
+    }
+
+    return (
+        <div className={styles.CalculateDuplicateFiles}>
+            <Row justify="start" align="middle">
+                <Col>
+                    <div className={styles.pageTitle} onClick={() => getFiles()}>路径: {fileInfo.path}</div>
+                </Col>
+                <Col>
+                    <Button type="primary" onClick={() => scanDirAll()}>
+                        开始
+                    </Button>
+                </Col>
+            </Row>
+
+            <div className={styles.stepsBox}>
+                <Steps
+                    current={current} percent={percent}
+                    labelPlacement="horizontal"
+                    direction="vertical"
+                    items={[
+                        {
+                            title: '扫描目录文件',
+                            status: stepsStatus.scanDir,
+                        },
+                        {
+                            title: '计算文件属性',
+                            status: stepsStatus.fileOptions,
+                        },
+                        {
+                            title: '分析重复文件',
+                            status: stepsStatus.duplicateFiles,
+                        },
+                        {
+                            title: '完成',
+                            status: stepsStatus.done,
+                        },
+                    ]}
+                />
+            </div>
+        </div>
+    );
+}

+ 9 - 2
src/pages/DuplicateFile/DuplicateFile.tsx

@@ -33,6 +33,8 @@ import { DEFAULT_TIME_FORMAT } from "@/config";
 
 import Database from "tauri-plugin-sql-api";
 import { createSql } from "@/databases/createTableSql";
+import {useRoutes} from "react-router";
+import {useNavigate} from "react-router-dom";
 const db = await Database.load("sqlite:test.db");
 const filesDB = await Database.load("sqlite:files.db");
 
@@ -48,6 +50,7 @@ export default function DuplicateFile() {
   const [isModalOpen, setIsModalOpen] = useState(false);
   const [fileInfo, setFileInfo] = useState<any>({});
   const [fileInfoSource, setFileInfoSource] = useState<FileInfoType>({});
+  const navigate = useNavigate()
 
   const columns = [
     {
@@ -109,7 +112,7 @@ export default function DuplicateFile() {
           <Button onClick={() => openModal(record)} type="default">
             修改
           </Button>
-          <Button type="primary">运行</Button>
+          <Button type="primary" onClick={() => calculateDuplicateFiles(record)}>运行</Button>
 
           <Popconfirm
               title="Delete the task"
@@ -192,7 +195,11 @@ export default function DuplicateFile() {
   const onPaginationChange: PaginationProps['onChange'] = (page) => {
     setCurrent(page);
   };
-  
+
+
+  function calculateDuplicateFiles(record: FileInfoType) {
+    navigate('calculate/' + record.id)
+  }
   return (
     <div className={styles.DuplicateFileBox}>
       <FileInfoEditer

+ 1 - 1
src/pages/Home/Home.tsx

@@ -1,6 +1,6 @@
 import {useState, useCallback, useEffect} from "react";
 import reactLogo from "../../assets/react.svg";
-import {invoke} from "core";
+import { invoke } from "@tauri-apps/api/tauri";
 // import {createDir, BaseDirectory} from '@tauri-apps/plugin-fs';
 // Create the `$APPDATA/users` directory
 import {  homeDir } from '@tauri-apps/api/path';

+ 3 - 4
src/plugins/tauri-plugin-file/file.ts

@@ -1,19 +1,18 @@
 import { invoke } from "@tauri-apps/api/tauri";
 
 import Database from "tauri-plugin-sql-api";
+import {FileInfoType} from "@/types/files";
 
 export class File {
   path: string;
 
   constructor(path: string) {
     this.path = path;
-    this.db = await Database.load("sqlite:files.db");
   }
 
-  static async getAllList(path: string): Promise<string[]> {
-    console.log(11,path);
+  static async getAllList(fileInfo: FileInfoType): Promise<string[]> {
     return await invoke<string[]>("plugin:st-files|get_all_directory", {
-      path,
+      fileInfo,
     });
   }
   static async getType(path: string): Promise<string> {

+ 25 - 0
src/services/file-service.ts

@@ -99,6 +99,31 @@ export async function get_info_by_path(path: string): Promise<[FileInfoType | bo
     }
 }
 
+/**
+ *
+ * @param path 文件的路径
+ * @returns FileInfoType
+ */
+export async function get_info_by_id(id: number): Promise<[FileInfoType | boolean, string]> {
+    try {
+        // await table_init(FILE_DB_PATH, "select_history");
+        // const DB = await SQLite.open(FILE_DB_PATH);
+        const DB = await Database.load("sqlite:files.db");
+        const res = await DB.select(
+            "SELECT * FROM select_history WHERE id = $1", [id]
+        );
+        if (Array.isArray(res)) {
+            return [res[0], ""];
+        }
+        return [false, "暂无数据"];
+    } catch (err) {
+        if (err && `${err}`.indexOf("UNIQUE constraint failed") > -1) {
+            return [false, "当前路径重复"];
+        }
+        return [false, `${err}`];
+    }
+}
+
 // export async function getSource(path: string) {
 
 // }

+ 10 - 0
src/types/files.d.ts

@@ -1,3 +1,5 @@
+import {StepProps} from "antd/es/steps";
+
 export interface FileInfoType {
   path?: string;
   checkboxAll?: boolean;
@@ -35,3 +37,11 @@ export type historyListType = {
   name: string;
   hash: string;
 };
+
+
+export type stepsStatusType = {
+  scanDir: StepProps.status;
+  fileOptions: StepProps.status;
+  duplicateFiles: StepProps.status;
+  done: StepProps.status;
+}