diff --git a/web/.umirc.ts b/web/.umirc.ts
index 5cbf1c0aa77a787f01d7cc663ad5eecfc270ccde..c4c622f15d3fab88ce0d199909dbc312e2756556 100644
--- a/web/.umirc.ts
+++ b/web/.umirc.ts
@@ -27,7 +27,7 @@ export default defineConfig({
   devtool: 'source-map',
   proxy: {
     '/v1': {
-      target: 'http://123.60.95.134:9380/',
+      target: 'http://192.168.200.233:9380/',
       changeOrigin: true,
       // pathRewrite: { '^/v1': '/v1' },
     },
diff --git a/web/externals.d.ts b/web/externals.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c5a8bc5ef5e56a3ac16c594acb81a694e6c9b05
--- /dev/null
+++ b/web/externals.d.ts
@@ -0,0 +1,138 @@
+// This file is generated by Umi automatically
+// DO NOT CHANGE IT MANUALLY!
+type CSSModuleClasses = { readonly [key: string]: string };
+declare module '*.css' {
+  const classes: CSSModuleClasses;
+  export default classes;
+}
+declare module '*.scss' {
+  const classes: CSSModuleClasses;
+  export default classes;
+}
+declare module '*.sass' {
+  const classes: CSSModuleClasses;
+  export default classes;
+}
+declare module '*.less' {
+  const classes: CSSModuleClasses;
+  export default classes;
+}
+declare module '*.styl' {
+  const classes: CSSModuleClasses;
+  export default classes;
+}
+declare module '*.stylus' {
+  const classes: CSSModuleClasses;
+  export default classes;
+}
+
+// images
+declare module '*.jpg' {
+  const src: string;
+  export default src;
+}
+declare module '*.jpeg' {
+  const src: string;
+  export default src;
+}
+declare module '*.png' {
+  const src: string;
+  export default src;
+}
+declare module '*.gif' {
+  const src: string;
+  export default src;
+}
+declare module '*.svg' {
+  import * as React from 'react';
+  export const ReactComponent: React.FunctionComponent<
+    React.SVGProps<SVGSVGElement> & { title?: string }
+  >;
+
+  const src: string;
+  export default src;
+}
+declare module '*.ico' {
+  const src: string;
+  export default src;
+}
+declare module '*.webp' {
+  const src: string;
+  export default src;
+}
+declare module '*.avif' {
+  const src: string;
+  export default src;
+}
+
+// media
+declare module '*.mp4' {
+  const src: string;
+  export default src;
+}
+declare module '*.webm' {
+  const src: string;
+  export default src;
+}
+declare module '*.ogg' {
+  const src: string;
+  export default src;
+}
+declare module '*.mp3' {
+  const src: string;
+  export default src;
+}
+declare module '*.wav' {
+  const src: string;
+  export default src;
+}
+declare module '*.flac' {
+  const src: string;
+  export default src;
+}
+declare module '*.aac' {
+  const src: string;
+  export default src;
+}
+
+// fonts
+declare module '*.woff' {
+  const src: string;
+  export default src;
+}
+declare module '*.woff2' {
+  const src: string;
+  export default src;
+}
+declare module '*.eot' {
+  const src: string;
+  export default src;
+}
+declare module '*.ttf' {
+  const src: string;
+  export default src;
+}
+declare module '*.otf' {
+  const src: string;
+  export default src;
+}
+
+// other
+declare module '*.wasm' {
+  const initWasm: (
+    options: WebAssembly.Imports,
+  ) => Promise<WebAssembly.Exports>;
+  export default initWasm;
+}
+declare module '*.webmanifest' {
+  const src: string;
+  export default src;
+}
+declare module '*.pdf' {
+  const src: string;
+  export default src;
+}
+declare module '*.txt' {
+  const src: string;
+  export default src;
+}
diff --git a/web/src/hooks/fileManagerHooks.ts b/web/src/hooks/fileManagerHooks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a9fd72c9b79798eb369254d47475475a77aca7f8
--- /dev/null
+++ b/web/src/hooks/fileManagerHooks.ts
@@ -0,0 +1,80 @@
+import { IFileListRequestBody } from '@/interfaces/request/file-manager';
+import { useCallback } from 'react';
+import { useDispatch, useSelector } from 'umi';
+
+export const useFetchFileList = () => {
+  const dispatch = useDispatch();
+
+  const fetchFileList = useCallback(
+    (payload: IFileListRequestBody) => {
+      return dispatch<any>({
+        type: 'fileManager/listFile',
+        payload,
+      });
+    },
+    [dispatch],
+  );
+
+  return fetchFileList;
+};
+
+export const useRemoveFile = () => {
+  const dispatch = useDispatch();
+
+  const removeFile = useCallback(
+    (fileIds: string[]) => {
+      return dispatch<any>({
+        type: 'fileManager/removeFile',
+        payload: { fileIds },
+      });
+    },
+    [dispatch],
+  );
+
+  return removeFile;
+};
+
+export const useRenameFile = () => {
+  const dispatch = useDispatch();
+
+  const renameFile = useCallback(
+    (fileId: string, name: string) => {
+      return dispatch<any>({
+        type: 'fileManager/renameFile',
+        payload: { fileId, name },
+      });
+    },
+    [dispatch],
+  );
+
+  return renameFile;
+};
+
+export const useFetchParentFolderList = () => {
+  const dispatch = useDispatch();
+
+  const fetchParentFolderList = useCallback(
+    (fileId: string) => {
+      return dispatch<any>({
+        type: 'fileManager/getAllParentFolder',
+        payload: { fileId },
+      });
+    },
+    [dispatch],
+  );
+
+  return fetchParentFolderList;
+};
+
+export const useSelectFileList = () => {
+  const fileList = useSelector((state) => state.fileManager.fileList);
+
+  return fileList;
+};
+
+export const useSelectParentFolderList = () => {
+  const parentFolderList = useSelector(
+    (state) => state.fileManager.parentFolderList,
+  );
+  return parentFolderList.toReversed();
+};
diff --git a/web/src/interfaces/database/file-manager.ts b/web/src/interfaces/database/file-manager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d22df392ce2fd4724f6c00dc877841d7e33cea64
--- /dev/null
+++ b/web/src/interfaces/database/file-manager.ts
@@ -0,0 +1,30 @@
+export interface IFile {
+  create_date: string;
+  create_time: number;
+  created_by: string;
+  id: string;
+  kb_ids: string[];
+  location: string;
+  name: string;
+  parent_id: string;
+  size: number;
+  tenant_id: string;
+  type: string;
+  update_date: string;
+  update_time: number;
+}
+
+export interface IFolder {
+  create_date: string;
+  create_time: number;
+  created_by: string;
+  id: string;
+  location: string;
+  name: string;
+  parent_id: string;
+  size: number;
+  tenant_id: string;
+  type: string;
+  update_date: string;
+  update_time: number;
+}
diff --git a/web/src/interfaces/request/base.ts b/web/src/interfaces/request/base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b780abe8decb2aad54e21db2b271200bf72cbf52
--- /dev/null
+++ b/web/src/interfaces/request/base.ts
@@ -0,0 +1,7 @@
+export interface IPaginationRequestBody {
+  keywords?: string;
+  page?: number;
+  page_size?: number; // name|create|doc_num|create_time|update_time,default:create_time
+  orderby?: string;
+  desc?: string;
+}
diff --git a/web/src/interfaces/request/file-manager.ts b/web/src/interfaces/request/file-manager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e748ca9c74ee165715ecbf90c7e406c092b5e6c
--- /dev/null
+++ b/web/src/interfaces/request/file-manager.ts
@@ -0,0 +1,5 @@
+import { IPaginationRequestBody } from './base';
+
+export interface IFileListRequestBody extends IPaginationRequestBody {
+  parent_id?: string; // folder id
+}
diff --git a/web/src/layouts/components/header/index.tsx b/web/src/layouts/components/header/index.tsx
index 3affb2282c061e69d4523d7fefd5c98305439ba4..2819f426c0383a90b8ae16ecffb149fc9ddc6844 100644
--- a/web/src/layouts/components/header/index.tsx
+++ b/web/src/layouts/components/header/index.tsx
@@ -1,4 +1,5 @@
 import { ReactComponent as StarIon } from '@/assets/svg/chat-star.svg';
+// import { ReactComponent as FileIcon } from '@/assets/svg/file-management.svg';
 import { ReactComponent as KnowledgeBaseIcon } from '@/assets/svg/knowledge-base.svg';
 import { ReactComponent as Logo } from '@/assets/svg/logo.svg';
 import { useTranslate } from '@/hooks/commonHooks';
diff --git a/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx b/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx
index f6fb5bef23c317bb8806ca7ccaca0db60551788b..b5f594069e578807741fb6e139b422f397e51960 100644
--- a/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx
@@ -182,7 +182,14 @@ const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
         ),
       },
     ];
-  }, [handleDelete, handleRunClick, handleCancelClick, t]);
+  }, [
+    handleDelete,
+    handleRunClick,
+    handleCancelClick,
+    t,
+    handleDisableClick,
+    handleEnableClick,
+  ]);
 
   return (
     <div className={styles.filter}>
diff --git a/web/src/pages/file-manager/action-cell/index.less b/web/src/pages/file-manager/action-cell/index.less
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/src/pages/file-manager/action-cell/index.tsx b/web/src/pages/file-manager/action-cell/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b6e44a28fde5debe6cab434483517186d3bb8559
--- /dev/null
+++ b/web/src/pages/file-manager/action-cell/index.tsx
@@ -0,0 +1,91 @@
+import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks';
+import { api_host } from '@/utils/api';
+import { downloadFile } from '@/utils/fileUtil';
+import {
+  DeleteOutlined,
+  DownloadOutlined,
+  EditOutlined,
+  ToolOutlined,
+} from '@ant-design/icons';
+import { Button, Space, Tooltip } from 'antd';
+
+import { useRemoveFile } from '@/hooks/fileManagerHooks';
+import { IFile } from '@/interfaces/database/file-manager';
+import styles from './index.less';
+
+interface IProps {
+  record: IFile;
+  setCurrentRecord: (record: any) => void;
+  showRenameModal: (record: IFile) => void;
+}
+
+const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => {
+  const documentId = record.id;
+  const beingUsed = false;
+  const { t } = useTranslate('knowledgeDetails');
+  const removeDocument = useRemoveFile();
+  const showDeleteConfirm = useShowDeleteConfirm();
+
+  const onRmDocument = () => {
+    if (!beingUsed) {
+      showDeleteConfirm({
+        onOk: () => {
+          return removeDocument([documentId]);
+        },
+      });
+    }
+  };
+
+  const onDownloadDocument = () => {
+    downloadFile({
+      url: `${api_host}/document/get/${documentId}`,
+      filename: record.name,
+    });
+  };
+
+  const setRecord = () => {
+    setCurrentRecord(record);
+  };
+
+  const onShowRenameModal = () => {
+    setRecord();
+    showRenameModal(record);
+  };
+
+  return (
+    <Space size={0}>
+      <Button type="text" className={styles.iconButton}>
+        <ToolOutlined size={20} />
+      </Button>
+
+      <Tooltip title={t('rename', { keyPrefix: 'common' })}>
+        <Button
+          type="text"
+          disabled={beingUsed}
+          onClick={onShowRenameModal}
+          className={styles.iconButton}
+        >
+          <EditOutlined size={20} />
+        </Button>
+      </Tooltip>
+      <Button
+        type="text"
+        disabled={beingUsed}
+        onClick={onRmDocument}
+        className={styles.iconButton}
+      >
+        <DeleteOutlined size={20} />
+      </Button>
+      <Button
+        type="text"
+        disabled={beingUsed}
+        onClick={onDownloadDocument}
+        className={styles.iconButton}
+      >
+        <DownloadOutlined size={20} />
+      </Button>
+    </Space>
+  );
+};
+
+export default ActionCell;
diff --git a/web/src/pages/file-manager/file-toolbar.tsx b/web/src/pages/file-manager/file-toolbar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cd01e6220786c9acb553557a3123c637e8f932ad
--- /dev/null
+++ b/web/src/pages/file-manager/file-toolbar.tsx
@@ -0,0 +1,153 @@
+import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg';
+import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks';
+import {
+  DownOutlined,
+  FileOutlined,
+  FileTextOutlined,
+  PlusOutlined,
+  SearchOutlined,
+} from '@ant-design/icons';
+import {
+  Breadcrumb,
+  BreadcrumbProps,
+  Button,
+  Dropdown,
+  Flex,
+  Input,
+  MenuProps,
+  Space,
+} from 'antd';
+import { useCallback, useMemo } from 'react';
+import {
+  useFetchDocumentListOnMount,
+  useGetPagination,
+  useHandleSearchChange,
+  useSelectBreadcrumbItems,
+} from './hooks';
+
+import { useRemoveFile } from '@/hooks/fileManagerHooks';
+import { Link } from 'umi';
+import styles from './index.less';
+
+interface IProps {
+  selectedRowKeys: string[];
+}
+
+const itemRender: BreadcrumbProps['itemRender'] = (
+  currentRoute,
+  params,
+  items,
+) => {
+  const isLast = currentRoute?.path === items[items.length - 1]?.path;
+
+  return isLast ? (
+    <span>{currentRoute.title}</span>
+  ) : (
+    <Link to={`${currentRoute.path}`}>{currentRoute.title}</Link>
+  );
+};
+
+const FileToolbar = ({ selectedRowKeys }: IProps) => {
+  const { t } = useTranslate('knowledgeDetails');
+  const { fetchDocumentList } = useFetchDocumentListOnMount();
+  const { setPagination, searchString } = useGetPagination(fetchDocumentList);
+  const { handleInputChange } = useHandleSearchChange(setPagination);
+  const removeDocument = useRemoveFile();
+  const showDeleteConfirm = useShowDeleteConfirm();
+  const breadcrumbItems = useSelectBreadcrumbItems();
+
+  const actionItems: MenuProps['items'] = useMemo(() => {
+    return [
+      {
+        key: '1',
+        label: (
+          <div>
+            <Button type="link">
+              <Space>
+                <FileTextOutlined />
+                {t('localFiles')}
+              </Space>
+            </Button>
+          </div>
+        ),
+      },
+      { type: 'divider' },
+      {
+        key: '2',
+        label: (
+          <div>
+            <Button type="link">
+              <FileOutlined />
+              {t('emptyFiles')}
+            </Button>
+          </div>
+        ),
+        // disabled: true,
+      },
+    ];
+  }, [t]);
+
+  const handleDelete = useCallback(() => {
+    showDeleteConfirm({
+      onOk: () => {
+        return removeDocument(selectedRowKeys);
+      },
+    });
+  }, [removeDocument, showDeleteConfirm, selectedRowKeys]);
+
+  const disabled = selectedRowKeys.length === 0;
+
+  const items: MenuProps['items'] = useMemo(() => {
+    return [
+      {
+        key: '4',
+        onClick: handleDelete,
+        label: (
+          <Flex gap={10}>
+            <span className={styles.deleteIconWrapper}>
+              <DeleteIcon width={18} />
+            </span>
+            <b>{t('delete', { keyPrefix: 'common' })}</b>
+          </Flex>
+        ),
+      },
+    ];
+  }, [handleDelete, t]);
+
+  return (
+    <div className={styles.filter}>
+      <Breadcrumb items={breadcrumbItems} itemRender={itemRender} />
+      <Space>
+        <Dropdown
+          menu={{ items }}
+          placement="bottom"
+          arrow={false}
+          disabled={disabled}
+        >
+          <Button>
+            <Space>
+              <b> {t('bulk')}</b>
+              <DownOutlined />
+            </Space>
+          </Button>
+        </Dropdown>
+        <Input
+          placeholder={t('searchFiles')}
+          value={searchString}
+          style={{ width: 220 }}
+          allowClear
+          onChange={handleInputChange}
+          prefix={<SearchOutlined />}
+        />
+
+        <Dropdown menu={{ items: actionItems }} trigger={['click']}>
+          <Button type="primary" icon={<PlusOutlined />}>
+            {t('addFile')}
+          </Button>
+        </Dropdown>
+      </Space>
+    </div>
+  );
+};
+
+export default FileToolbar;
diff --git a/web/src/pages/file-manager/hooks.ts b/web/src/pages/file-manager/hooks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a43c336456f232986f66f3572be765d2c3dd496
--- /dev/null
+++ b/web/src/pages/file-manager/hooks.ts
@@ -0,0 +1,193 @@
+import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
+import {
+  useFetchFileList,
+  useFetchParentFolderList,
+  useRenameFile,
+  useSelectFileList,
+  useSelectParentFolderList,
+} from '@/hooks/fileManagerHooks';
+import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
+import { Pagination } from '@/interfaces/common';
+import { IFile } from '@/interfaces/database/file-manager';
+import { PaginationProps } from 'antd';
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { useDispatch, useNavigate, useSearchParams, useSelector } from 'umi';
+
+export const useGetFolderId = () => {
+  const [searchParams] = useSearchParams();
+  const id = searchParams.get('folderId') as string;
+
+  return id;
+};
+
+export const useFetchDocumentListOnMount = () => {
+  const fetchDocumentList = useFetchFileList();
+  const fileList = useSelectFileList();
+  const id = useGetFolderId();
+
+  const dispatch = useDispatch();
+
+  useEffect(() => {
+    fetchDocumentList({ parent_id: id });
+  }, [dispatch, fetchDocumentList, id]);
+
+  return { fetchDocumentList, fileList };
+};
+
+export const useGetPagination = (
+  fetchDocumentList: (payload: IFile) => any,
+) => {
+  const dispatch = useDispatch();
+  const kFModel = useSelector((state: any) => state.kFModel);
+  const { t } = useTranslate('common');
+
+  const setPagination = useCallback(
+    (pageNumber = 1, pageSize?: number) => {
+      const pagination: Pagination = {
+        current: pageNumber,
+      } as Pagination;
+      if (pageSize) {
+        pagination.pageSize = pageSize;
+      }
+      dispatch({
+        type: 'kFModel/setPagination',
+        payload: pagination,
+      });
+    },
+    [dispatch],
+  );
+
+  const onPageChange: PaginationProps['onChange'] = useCallback(
+    (pageNumber: number, pageSize: number) => {
+      setPagination(pageNumber, pageSize);
+      fetchDocumentList();
+    },
+    [fetchDocumentList, setPagination],
+  );
+
+  const pagination: PaginationProps = useMemo(() => {
+    return {
+      showQuickJumper: true,
+      total: kFModel.total,
+      showSizeChanger: true,
+      current: kFModel.pagination.current,
+      pageSize: kFModel.pagination.pageSize,
+      pageSizeOptions: [1, 2, 10, 20, 50, 100],
+      onChange: onPageChange,
+      showTotal: (total) => `${t('total')} ${total}`,
+    };
+  }, [kFModel, onPageChange, t]);
+
+  return {
+    pagination,
+    setPagination,
+    total: kFModel.total,
+    searchString: kFModel.searchString,
+  };
+};
+
+export const useHandleSearchChange = (setPagination: () => void) => {
+  const dispatch = useDispatch();
+
+  const throttledGetDocumentList = useCallback(() => {
+    dispatch({
+      type: 'kFModel/throttledGetDocumentList',
+    });
+  }, [dispatch]);
+
+  const handleInputChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
+      const value = e.target.value;
+      dispatch({ type: 'kFModel/setSearchString', payload: value });
+      setPagination();
+      throttledGetDocumentList();
+    },
+    [setPagination, throttledGetDocumentList, dispatch],
+  );
+
+  return { handleInputChange };
+};
+
+export const useGetRowSelection = () => {
+  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
+
+  const rowSelection = {
+    selectedRowKeys,
+    onChange: (newSelectedRowKeys: React.Key[]) => {
+      setSelectedRowKeys(newSelectedRowKeys);
+    },
+  };
+
+  return rowSelection;
+};
+
+export const useNavigateToOtherFolder = () => {
+  const navigate = useNavigate();
+  const navigateToOtherFolder = useCallback(
+    (folderId: string) => {
+      navigate(`/file?folderId=${folderId}`);
+    },
+    [navigate],
+  );
+
+  return navigateToOtherFolder;
+};
+
+export const useRenameCurrentFile = () => {
+  const [file, setFile] = useState<IFile>({} as IFile);
+  const {
+    visible: fileRenameVisible,
+    hideModal: hideFileRenameModal,
+    showModal: showFileRenameModal,
+  } = useSetModalState();
+  const renameFile = useRenameFile();
+
+  const onFileRenameOk = useCallback(
+    async (name: string) => {
+      const ret = await renameFile(file.id, name);
+
+      if (ret === 0) {
+        hideFileRenameModal();
+      }
+    },
+    [renameFile, file, hideFileRenameModal],
+  );
+
+  const loading = useOneNamespaceEffectsLoading('fileManager', ['renameFile']);
+
+  const handleShowFileRenameModal = useCallback(
+    async (record: IFile) => {
+      setFile(record);
+      showFileRenameModal();
+    },
+    [showFileRenameModal],
+  );
+
+  return {
+    fileRenameLoading: loading,
+    initialFileName: file.name,
+    onFileRenameOk,
+    fileRenameVisible,
+    hideFileRenameModal,
+    showFileRenameModal: handleShowFileRenameModal,
+  };
+};
+
+export const useSelectBreadcrumbItems = () => {
+  const parentFolderList = useSelectParentFolderList();
+  const id = useGetFolderId();
+  const fetchParentFolderList = useFetchParentFolderList();
+
+  useEffect(() => {
+    if (id) {
+      fetchParentFolderList(id);
+    }
+  }, [id, fetchParentFolderList]);
+
+  return parentFolderList.length === 1
+    ? []
+    : parentFolderList.map((x) => ({
+        title: x.name === '/' ? 'root' : x.name,
+        path: `/file?folderId=${x.id}`,
+      }));
+};
diff --git a/web/src/pages/file-manager/index.less b/web/src/pages/file-manager/index.less
new file mode 100644
index 0000000000000000000000000000000000000000..566c63a947ce663ab1e5a5778997e4a8283d42de
--- /dev/null
+++ b/web/src/pages/file-manager/index.less
@@ -0,0 +1,18 @@
+.fileManagerWrapper {
+  flex-basis: 100%;
+  padding: 32px;
+}
+
+.filter {
+  height: 32px;
+  display: flex;
+  margin: 10px 0;
+  justify-content: space-between;
+  padding: 24px 0;
+  align-items: center;
+}
+
+.deleteIconWrapper {
+  width: 22px;
+  text-align: center;
+}
diff --git a/web/src/pages/file-manager/index.tsx b/web/src/pages/file-manager/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..83dee7a6345c4a2fc741a3ed2b80914c063b762b
--- /dev/null
+++ b/web/src/pages/file-manager/index.tsx
@@ -0,0 +1,99 @@
+import { useSelectFileList } from '@/hooks/fileManagerHooks';
+import { IFile } from '@/interfaces/database/file-manager';
+import { formatDate } from '@/utils/date';
+import { Button, Table } from 'antd';
+import { ColumnsType } from 'antd/es/table';
+import ActionCell from './action-cell';
+import FileToolbar from './file-toolbar';
+import {
+  useGetRowSelection,
+  useNavigateToOtherFolder,
+  useRenameCurrentFile,
+} from './hooks';
+
+import RenameModal from '@/components/rename-modal';
+import styles from './index.less';
+
+const FileManager = () => {
+  const fileList = useSelectFileList();
+  const rowSelection = useGetRowSelection();
+  const navigateToOtherFolder = useNavigateToOtherFolder();
+  const {
+    fileRenameVisible,
+    fileRenameLoading,
+    hideFileRenameModal,
+    showFileRenameModal,
+    initialFileName,
+    onFileRenameOk,
+  } = useRenameCurrentFile();
+
+  const columns: ColumnsType<IFile> = [
+    {
+      title: 'Name',
+      dataIndex: 'name',
+      key: 'name',
+      render(value, record) {
+        return record.type === 'folder' ? (
+          <Button
+            type={'link'}
+            onClick={() => navigateToOtherFolder(record.id)}
+          >
+            {value}
+          </Button>
+        ) : (
+          value
+        );
+      },
+    },
+    {
+      title: 'Upload Date',
+      dataIndex: 'create_date',
+      key: 'create_date',
+      render(text) {
+        return formatDate(text);
+      },
+    },
+    {
+      title: 'Location',
+      dataIndex: 'location',
+      key: 'location',
+    },
+    {
+      title: 'Action',
+      dataIndex: 'action',
+      key: 'action',
+      render: (text, record) => (
+        <ActionCell
+          record={record}
+          setCurrentRecord={(record: any) => {
+            console.info(record);
+          }}
+          showRenameModal={showFileRenameModal}
+        ></ActionCell>
+      ),
+    },
+  ];
+
+  return (
+    <section className={styles.fileManagerWrapper}>
+      <FileToolbar
+        selectedRowKeys={rowSelection.selectedRowKeys as string[]}
+      ></FileToolbar>
+      <Table
+        dataSource={fileList}
+        columns={columns}
+        rowKey={'id'}
+        rowSelection={rowSelection}
+      />
+      <RenameModal
+        visible={fileRenameVisible}
+        hideModal={hideFileRenameModal}
+        onOk={onFileRenameOk}
+        initialName={initialFileName}
+        loading={fileRenameLoading}
+      ></RenameModal>
+    </section>
+  );
+};
+
+export default FileManager;
diff --git a/web/src/pages/file-manager/model.ts b/web/src/pages/file-manager/model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e58faa9d5f1528599e21916d13c0d239ecb32f9b
--- /dev/null
+++ b/web/src/pages/file-manager/model.ts
@@ -0,0 +1,65 @@
+import { IFile, IFolder } from '@/interfaces/database/file-manager';
+import fileManagerService from '@/services/fileManagerService';
+import { DvaModel } from 'umi';
+
+export interface FileManagerModelState {
+  fileList: IFile[];
+  parentFolderList: IFolder[];
+}
+
+const model: DvaModel<FileManagerModelState> = {
+  namespace: 'fileManager',
+  state: { fileList: [], parentFolderList: [] },
+  reducers: {
+    setFileList(state, { payload }) {
+      return { ...state, fileList: payload };
+    },
+    setParentFolderList(state, { payload }) {
+      return { ...state, parentFolderList: payload };
+    },
+  },
+  effects: {
+    *removeFile({ payload = {} }, { call, put }) {
+      const { data } = yield call(fileManagerService.removeFile, payload);
+      const { retcode } = data;
+      if (retcode === 0) {
+        yield put({
+          type: 'listFile',
+          payload: data.data?.files ?? [],
+        });
+      }
+    },
+    *listFile({ payload = {} }, { call, put }) {
+      const { data } = yield call(fileManagerService.listFile, payload);
+      const { retcode, data: res } = data;
+
+      if (retcode === 0 && Array.isArray(res.files)) {
+        yield put({
+          type: 'setFileList',
+          payload: res.files,
+        });
+      }
+    },
+    *renameFile({ payload = {} }, { call, put }) {
+      const { data } = yield call(fileManagerService.renameFile, payload);
+      if (data.retcode === 0) {
+        yield put({ type: 'listFile' });
+      }
+      return data.retcode;
+    },
+    *getAllParentFolder({ payload = {} }, { call, put }) {
+      const { data } = yield call(
+        fileManagerService.getAllParentFolder,
+        payload,
+      );
+      if (data.retcode === 0) {
+        yield put({
+          type: 'setParentFolderList',
+          payload: data.data?.parent_folders ?? [],
+        });
+      }
+      return data.retcode;
+    },
+  },
+};
+export default model;
diff --git a/web/src/pages/file/index.tsx b/web/src/pages/file/index.tsx
deleted file mode 100644
index be7eada21b32db334e3b5710f282cad5506bf9d7..0000000000000000000000000000000000000000
--- a/web/src/pages/file/index.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { UploadOutlined } from '@ant-design/icons';
-import { Button, Upload } from 'antd';
-import React, { useEffect, useState } from 'react';
-
-const File: React.FC = () => {
-  const [fileList, setFileList] = useState([
-    {
-      uid: '0',
-      name: 'xxx.png',
-      status: 'uploading',
-      percent: 10,
-    },
-  ]);
-  const obj = {
-    uid: '-1',
-    name: 'yyy.png',
-    status: 'done',
-    url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-    thumbUrl:
-      'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-  };
-  useEffect(() => {
-    const timer = setInterval(() => {
-      setFileList((fileList: any) => {
-        const percent = fileList[0]?.percent;
-        if (percent + 10 >= 100) {
-          clearInterval(timer);
-          return [obj];
-        }
-        const list = [{ ...fileList[0], percent: percent + 10 }];
-        console.log(list);
-        return list;
-      });
-    }, 300);
-  }, []);
-  return (
-    <>
-      <Upload
-        action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188"
-        listType="picture"
-        fileList={[...fileList]}
-        multiple
-      >
-        <Button icon={<UploadOutlined />}>Upload</Button>
-      </Upload>
-    </>
-  );
-};
-
-export default File;
diff --git a/web/src/pages/login/index.tsx b/web/src/pages/login/index.tsx
index 98700dc9afc6d633aefcb023640aeaa3c7239340..406bed0c8ff1a83a84f044bc1d6e710ed6648b2f 100644
--- a/web/src/pages/login/index.tsx
+++ b/web/src/pages/login/index.tsx
@@ -167,20 +167,22 @@ const Login = () => {
                     Sign in with Google
                   </div>
                 </Button> */}
-                <Button
-                  block
-                  size="large"
-                  onClick={toGoogle}
-                  style={{ marginTop: 15 }}
-                >
-                  <div>
-                    <Icon
-                      icon="local:github"
-                      style={{ verticalAlign: 'middle', marginRight: 5 }}
-                    />
-                    Sign in with Github
-                  </div>
-                </Button>
+                {location.host === 'demo.ragflow.io' && (
+                  <Button
+                    block
+                    size="large"
+                    onClick={toGoogle}
+                    style={{ marginTop: 15 }}
+                  >
+                    <div>
+                      <Icon
+                        icon="local:github"
+                        style={{ verticalAlign: 'middle', marginRight: 5 }}
+                      />
+                      Sign in with Github
+                    </div>
+                  </Button>
+                )}
               </>
             )}
           </Form>
diff --git a/web/src/routes.ts b/web/src/routes.ts
index ff6187052057b33c392153caecd3cb33447665c3..5900f98954bb544e4e0b9a38f47e73a1be661f38 100644
--- a/web/src/routes.ts
+++ b/web/src/routes.ts
@@ -82,7 +82,7 @@ const routes = [
       },
       {
         path: '/file',
-        component: '@/pages/file',
+        component: '@/pages/file-manager',
       },
     ],
   },
diff --git a/web/src/services/fileManagerService.ts b/web/src/services/fileManagerService.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9edcc2f79763b5327cbe84f976ace62e427ee310
--- /dev/null
+++ b/web/src/services/fileManagerService.ts
@@ -0,0 +1,36 @@
+import api from '@/utils/api';
+import registerServer from '@/utils/registerServer';
+import request from '@/utils/request';
+
+const { listFile, removeFile, uploadFile, renameFile, getAllParentFolder } =
+  api;
+
+const methods = {
+  listFile: {
+    url: listFile,
+    method: 'get',
+  },
+  removeFile: {
+    url: removeFile,
+    method: 'post',
+  },
+  uploadFile: {
+    url: uploadFile,
+    method: 'post',
+  },
+  renameFile: {
+    url: renameFile,
+    method: 'post',
+  },
+  getAllParentFolder: {
+    url: getAllParentFolder,
+    method: 'get',
+  },
+} as const;
+
+const fileManagerService = registerServer<keyof typeof methods>(
+  methods,
+  request,
+);
+
+export default fileManagerService;
diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts
index f404eceeae2c8fd51df4fc656eb78d50de2e7408..f449f932c847fe0d91d7618d573af6ad7b312b37 100644
--- a/web/src/utils/api.ts
+++ b/web/src/utils/api.ts
@@ -66,4 +66,11 @@ export default {
   createExternalConversation: `${api_host}/api/new_conversation`,
   getExternalConversation: `${api_host}/api/conversation`,
   completeExternalConversation: `${api_host}/api/completion`,
+
+  // file manager
+  listFile: `${api_host}/file/list`,
+  uploadFile: `${api_host}/file/upload`,
+  removeFile: `${api_host}/file/rm`,
+  renameFile: `${api_host}/file/rename`,
+  getAllParentFolder: `${api_host}/file/all_parent_folder`,
 };
diff --git a/web/src/utils/commonUtil.ts b/web/src/utils/commonUtil.ts
index 9cda1a99584a638efbd62ff6cacf72652d75bc52..cd6c0d5c74b4d006effdd0b7c55e4cc29447ea4d 100644
--- a/web/src/utils/commonUtil.ts
+++ b/web/src/utils/commonUtil.ts
@@ -5,11 +5,18 @@ export const isFormData = (data: unknown): data is FormData => {
   return data instanceof FormData;
 };
 
+const excludedFields = ['img2txt_id'];
+
+const isExcludedField = (key: string) => {
+  return excludedFields.includes(key);
+};
+
 export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
   if (isObject(data) && !isFormData(data)) {
     return Object.keys(data).reduce<Record<string, any>>((pre, cur) => {
       const value = (data as Record<string, any>)[cur];
-      pre[isFormData(value) ? cur : snakeCase(cur)] = value;
+      pre[isFormData(value) || isExcludedField(cur) ? cur : snakeCase(cur)] =
+        value;
       return pre;
     }, {});
   }
diff --git a/web/typings.d.ts b/web/typings.d.ts
index cbe06bea1f4020f78d98729b3a84c01ebbb25219..682c112a36b1caaa8524e37d3aed50a1264687e6 100644
--- a/web/typings.d.ts
+++ b/web/typings.d.ts
@@ -1,8 +1,39 @@
-import 'umi/typings';
+import { ChunkModelState } from '@/pages/add-knowledge/components/knowledge-chunk/model';
+import { KFModelState } from '@/pages/add-knowledge/components/knowledge-file/model';
+import { KSModelState } from '@/pages/add-knowledge/components/knowledge-setting/model';
+import { TestingModelState } from '@/pages/add-knowledge/components/knowledge-testing/model';
+import { kAModelState } from '@/pages/add-knowledge/model';
+import { ChatModelState } from '@/pages/chat/model';
+import { FileManagerModelState } from '@/pages/file-manager/model';
+import { KnowledgeModelState } from '@/pages/knowledge/model';
+import { LoginModelState } from '@/pages/login/model';
+import { SettingModelState } from '@/pages/user-setting/model';
+
 declare module 'lodash';
 
-// declare type Nullable<T> = T | null; invalid
+function useSelector<TState = RootState, TSelected = unknown>(
+  selector: (state: TState) => TSelected,
+  equalityFn?: (left: TSelected, right: TSelected) => boolean,
+): TSelected;
+
+export interface RootState {
+  // loading: Loading;
+  fileManager: FileManagerModelState;
+  chatModel: ChatModelState;
+  loginModel: LoginModelState;
+  knowledgeModel: KnowledgeModelState;
+  settingModel: SettingModelState;
+  kFModel: KFModelState;
+  kAModel: kAModelState;
+  chunkModel: ChunkModelState;
+  kSModel: KSModelState;
+  testingModel: TestingModelState;
+}
 
 declare global {
   type Nullable<T> = T | null;
 }
+
+declare module 'umi' {
+  export { useSelector };
+}