diff --git a/web/.umirc.ts b/web/.umirc.ts
index 7b36b570042d77895278c62043d3107693574c6d..5cbf1c0aa77a787f01d7cc663ad5eecfc270ccde 100644
--- a/web/.umirc.ts
+++ b/web/.umirc.ts
@@ -12,11 +12,13 @@ export default defineConfig({
   icons: {},
   hash: true,
   favicons: ['/logo.svg'],
+  clickToComponent: {},
   history: {
     type: 'browser',
   },
   plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'],
   dva: {},
+
   lessLoader: {
     modifyVars: {
       hack: `true; @import "~@/less/index.less";`,
diff --git a/web/src/app.tsx b/web/src/app.tsx
index a67e507ec44658a243f39f10a435abc2dd98b451..51ce7564d2ca445ab0403ca879f6b0b31076f7de 100644
--- a/web/src/app.tsx
+++ b/web/src/app.tsx
@@ -1,6 +1,16 @@
+import { ConfigProvider } from 'antd';
 import React, { ReactNode } from 'react';
-import { Inspector } from 'react-dev-inspector';
 
 export function rootContainer(container: ReactNode) {
-  return React.createElement(Inspector, null, container);
+  return React.createElement(
+    ConfigProvider,
+    {
+      theme: {
+        token: {
+          fontFamily: 'Inter',
+        },
+      },
+    },
+    container,
+  );
 }
diff --git a/web/src/assets/inter/Inter-Black.woff2 b/web/src/assets/inter/Inter-Black.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..18b35db75c504d429820dba1aa2c99987978be10
Binary files /dev/null and b/web/src/assets/inter/Inter-Black.woff2 differ
diff --git a/web/src/assets/inter/Inter-BlackItalic.woff2 b/web/src/assets/inter/Inter-BlackItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..02c9d8ecc2fbff75cfb63e1f1d8916e52dbc5c1e
Binary files /dev/null and b/web/src/assets/inter/Inter-BlackItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-Bold.woff2 b/web/src/assets/inter/Inter-Bold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..0f1b157633c5f8485fe9e79585daa89666cc07f6
Binary files /dev/null and b/web/src/assets/inter/Inter-Bold.woff2 differ
diff --git a/web/src/assets/inter/Inter-BoldItalic.woff2 b/web/src/assets/inter/Inter-BoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..bc50f24c8731ec8cbf75effb9cdc2b6129b65dfb
Binary files /dev/null and b/web/src/assets/inter/Inter-BoldItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-ExtraBold.woff2 b/web/src/assets/inter/Inter-ExtraBold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..b1133688a43130b5c96bcd2307bb7f6127241412
Binary files /dev/null and b/web/src/assets/inter/Inter-ExtraBold.woff2 differ
diff --git a/web/src/assets/inter/Inter-ExtraBoldItalic.woff2 b/web/src/assets/inter/Inter-ExtraBoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..a5b76ca8daf9ed071fd40f226c38814adaa619dd
Binary files /dev/null and b/web/src/assets/inter/Inter-ExtraBoldItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-ExtraLight.woff2 b/web/src/assets/inter/Inter-ExtraLight.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..1d77ae8d0414b1672f29b617d179fddc66273b70
Binary files /dev/null and b/web/src/assets/inter/Inter-ExtraLight.woff2 differ
diff --git a/web/src/assets/inter/Inter-ExtraLightItalic.woff2 b/web/src/assets/inter/Inter-ExtraLightItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..8c6849209dccd480d11ee355c72b8e7a60ceb7ed
Binary files /dev/null and b/web/src/assets/inter/Inter-ExtraLightItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-Italic.woff2 b/web/src/assets/inter/Inter-Italic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..4c24ce2815261b7ccd4c58f402ae5c9de860dddc
Binary files /dev/null and b/web/src/assets/inter/Inter-Italic.woff2 differ
diff --git a/web/src/assets/inter/Inter-Light.woff2 b/web/src/assets/inter/Inter-Light.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..dbe61437a17faaaccc11a626f3655c3e497b61a1
Binary files /dev/null and b/web/src/assets/inter/Inter-Light.woff2 differ
diff --git a/web/src/assets/inter/Inter-LightItalic.woff2 b/web/src/assets/inter/Inter-LightItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..a40d042158df1f0708611af81defcb2846360985
Binary files /dev/null and b/web/src/assets/inter/Inter-LightItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-Medium.woff2 b/web/src/assets/inter/Inter-Medium.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..0fd2ee7370b34dd5628eac3653b541ce0508f154
Binary files /dev/null and b/web/src/assets/inter/Inter-Medium.woff2 differ
diff --git a/web/src/assets/inter/Inter-MediumItalic.woff2 b/web/src/assets/inter/Inter-MediumItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..96767155d900aae798a4208e57e29c915110e6d3
Binary files /dev/null and b/web/src/assets/inter/Inter-MediumItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-Regular.woff2 b/web/src/assets/inter/Inter-Regular.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..b8699af29b021cbbbdf82e18f5c4a2271d19b616
Binary files /dev/null and b/web/src/assets/inter/Inter-Regular.woff2 differ
diff --git a/web/src/assets/inter/Inter-SemiBold.woff2 b/web/src/assets/inter/Inter-SemiBold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..95c48b184ea96eeeba1eb03904abf1c587f848b6
Binary files /dev/null and b/web/src/assets/inter/Inter-SemiBold.woff2 differ
diff --git a/web/src/assets/inter/Inter-SemiBoldItalic.woff2 b/web/src/assets/inter/Inter-SemiBoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..ddfe19e839c05f579914dae39743c7e726fea1f7
Binary files /dev/null and b/web/src/assets/inter/Inter-SemiBoldItalic.woff2 differ
diff --git a/web/src/assets/inter/Inter-Thin.woff2 b/web/src/assets/inter/Inter-Thin.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..07909608cd4a42ee15873c38c4b04233f2b9852c
Binary files /dev/null and b/web/src/assets/inter/Inter-Thin.woff2 differ
diff --git a/web/src/assets/inter/Inter-ThinItalic.woff2 b/web/src/assets/inter/Inter-ThinItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..a7bf2138010ed291c3b6cf2abc21a3f704a9593d
Binary files /dev/null and b/web/src/assets/inter/Inter-ThinItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Black.woff2 b/web/src/assets/inter/InterDisplay-Black.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..8138123c13677320ff31fcdcafbf8d64bf6e12ea
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Black.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-BlackItalic.woff2 b/web/src/assets/inter/InterDisplay-BlackItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..735ba21f99f08ea0c6ae6ac79449f5f261fdd395
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-BlackItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Bold.woff2 b/web/src/assets/inter/InterDisplay-Bold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..11c67196e1727aaa9164e43f40bab3c23558bb65
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Bold.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-BoldItalic.woff2 b/web/src/assets/inter/InterDisplay-BoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..5b6a1fb006f6d7479b8908cb7e91f397e2edcc5d
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-BoldItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-ExtraBold.woff2 b/web/src/assets/inter/InterDisplay-ExtraBold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..9058e98613463231af7f890812e815c6a2a2e0ae
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-ExtraBold.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-ExtraBoldItalic.woff2 b/web/src/assets/inter/InterDisplay-ExtraBoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..4cd61c0672533019d45fbd72f8c2b09313e40fb8
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-ExtraBoldItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-ExtraLight.woff2 b/web/src/assets/inter/InterDisplay-ExtraLight.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..8621b2995101721e4dba7974271ecbd75a4ff27d
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-ExtraLight.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-ExtraLightItalic.woff2 b/web/src/assets/inter/InterDisplay-ExtraLightItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..689c8d9ca1dc840f590c787123f244b69c0c5e02
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-ExtraLightItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Italic.woff2 b/web/src/assets/inter/InterDisplay-Italic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..11f20bc821e4fe3388a13d743da42f6555d9dcf0
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Italic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Light.woff2 b/web/src/assets/inter/InterDisplay-Light.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..446301c35ebbd039af2a818652fb26d2d9d99109
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Light.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-LightItalic.woff2 b/web/src/assets/inter/InterDisplay-LightItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..f688196103030feb3f3931a5e952a72ae4cca6e1
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-LightItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Medium.woff2 b/web/src/assets/inter/InterDisplay-Medium.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..29160b2c5deecae0ae4e5580571eb8323da638cf
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Medium.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-MediumItalic.woff2 b/web/src/assets/inter/InterDisplay-MediumItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..ef1bcbe3c71e24a904d7088146632f53bf640edf
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-MediumItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Regular.woff2 b/web/src/assets/inter/InterDisplay-Regular.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..a6c04f68ecab682d79ad6b00022067b5a6e53a85
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Regular.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-SemiBold.woff2 b/web/src/assets/inter/InterDisplay-SemiBold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..2b4db239b47f7bd629683f494965b1305c0ae107
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-SemiBold.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-SemiBoldItalic.woff2 b/web/src/assets/inter/InterDisplay-SemiBoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..59091db3111c957705e732889f02a311caa218c2
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-SemiBoldItalic.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-Thin.woff2 b/web/src/assets/inter/InterDisplay-Thin.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..dc0b948607430e9e04e2b30f74a6f0bdc7a1c756
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-Thin.woff2 differ
diff --git a/web/src/assets/inter/InterDisplay-ThinItalic.woff2 b/web/src/assets/inter/InterDisplay-ThinItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..96439c0c08e6e5aa62e7565c1a48398f07e1ec07
Binary files /dev/null and b/web/src/assets/inter/InterDisplay-ThinItalic.woff2 differ
diff --git a/web/src/assets/inter/InterVariable-Italic.woff2 b/web/src/assets/inter/InterVariable-Italic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..f22ec255493ebf94d16270d6279c1c9f2001278c
Binary files /dev/null and b/web/src/assets/inter/InterVariable-Italic.woff2 differ
diff --git a/web/src/assets/inter/InterVariable.woff2 b/web/src/assets/inter/InterVariable.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..22a12b04e1abd959c00f9d8429ca78ddae3f7dde
Binary files /dev/null and b/web/src/assets/inter/InterVariable.woff2 differ
diff --git a/web/src/components/similarity-slider/index.tsx b/web/src/components/similarity-slider/index.tsx
index 1c5d49be6e267d74647386daf48b00ad91a4e37b..7f60199911f172c601346afc38daa6e3adad68b0 100644
--- a/web/src/components/similarity-slider/index.tsx
+++ b/web/src/components/similarity-slider/index.tsx
@@ -15,7 +15,7 @@ const SimilaritySlider = ({ isTooltipShown = false }: IProps) => {
       <Form.Item<FieldType>
         label="Similarity threshold"
         name={'similarity_threshold'}
-        tooltip={isTooltipShown && 'xxx'}
+        tooltip={isTooltipShown && 'coming soon'}
         initialValue={0.2}
       >
         <Slider max={1} step={0.01} />
@@ -24,7 +24,7 @@ const SimilaritySlider = ({ isTooltipShown = false }: IProps) => {
         label="Vector similarity weight"
         name={'vector_similarity_weight'}
         initialValue={0.3}
-        tooltip={isTooltipShown && 'xxx'}
+        tooltip={isTooltipShown && 'coming soon'}
       >
         <Slider max={1} step={0.01} />
       </Form.Item>
diff --git a/web/src/global.less b/web/src/global.less
new file mode 100644
index 0000000000000000000000000000000000000000..70714eae7961077313e6f6a5f63a31ce6cab31c8
--- /dev/null
+++ b/web/src/global.less
@@ -0,0 +1,5 @@
+@import url(./inter.less);
+
+body {
+  font-family: Inter;
+}
diff --git a/web/src/hooks/authHook.ts b/web/src/hooks/authHook.ts
index d4bf85fc1b2aff319faefa3d8f6fd0e66492610c..114257d6cb6d0c268e68e413d0b835799cc9f8a5 100644
--- a/web/src/hooks/authHook.ts
+++ b/web/src/hooks/authHook.ts
@@ -1,7 +1,6 @@
 import authorizationUtil from '@/utils/authorizationUtil';
 import { message } from 'antd';
 import { useEffect, useMemo, useState } from 'react';
-import { Nullable } from 'typings';
 import { useNavigate, useSearchParams } from 'umi';
 
 export const useLoginWithGithub = () => {
diff --git a/web/src/inter.less b/web/src/inter.less
new file mode 100644
index 0000000000000000000000000000000000000000..aa8e2f331121bca3f0d9ec32ddb09ca5180cccb1
--- /dev/null
+++ b/web/src/inter.less
@@ -0,0 +1,273 @@
+/* Variable fonts usage:
+:root { font-family: "Inter", sans-serif; }
+@supports (font-variation-settings: normal) {
+  :root { font-family: "InterVariable", sans-serif; font-optical-sizing: auto; }
+} */
+@font-face {
+  font-family: InterVariable;
+  font-style: normal;
+  font-weight: 100 900;
+  font-display: swap;
+  src: url('@/assets/inter/InterVariable.woff2') format('woff2');
+}
+@font-face {
+  font-family: InterVariable;
+  font-style: italic;
+  font-weight: 100 900;
+  font-display: swap;
+  src: url('@/assets/inter/InterVariable-Italic.woff2') format('woff2');
+}
+
+/* static fonts */
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 100;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Thin.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 100;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-ThinItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 200;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-ExtraLight.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 200;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-ExtraLightItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 300;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Light.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 300;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-LightItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Regular.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 400;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Italic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 500;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Medium.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 500;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-MediumItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 600;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-SemiBold.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 600;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-SemiBoldItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 700;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Bold.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 700;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-BoldItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 800;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-ExtraBold.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 800;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-ExtraBoldItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: normal;
+  font-weight: 900;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-Black.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'Inter';
+  font-style: italic;
+  font-weight: 900;
+  font-display: swap;
+  src: url('@/assets/inter/Inter-BlackItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 100;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Thin.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 100;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-ThinItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 200;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-ExtraLight.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 200;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-ExtraLightItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 300;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Light.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 300;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-LightItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Regular.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 400;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Italic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 500;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Medium.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 500;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-MediumItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 600;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-SemiBold.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 600;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-SemiBoldItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 700;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Bold.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 700;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-BoldItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 800;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-ExtraBold.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 800;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-ExtraBoldItalic.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: normal;
+  font-weight: 900;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-Black.woff2') format('woff2');
+}
+@font-face {
+  font-family: 'InterDisplay';
+  font-style: italic;
+  font-weight: 900;
+  font-display: swap;
+  src: url('@/assets/inter/InterDisplay-BlackItalic.woff2') format('woff2');
+}
diff --git a/web/src/layouts/components/header/index.less b/web/src/layouts/components/header/index.less
index 93cc794f67ffa116f80f0745d521d07f1abd342c..ffbe6b0b940844abc7359ae596cd165f3fd39f7a 100644
--- a/web/src/layouts/components/header/index.less
+++ b/web/src/layouts/components/header/index.less
@@ -36,7 +36,6 @@
     border: 0 !important;
     background-color: rgba(249, 249, 249, 1);
     font-weight: @fontWeight700;
-    font-family: 'Nunito Sans';
     color: rgba(29, 25, 41, 1);
     &::before {
       display: none !important;
diff --git a/web/src/less/variable.less b/web/src/less/variable.less
index bed5e6fcf81141a47af07086ec787a24cd88103d..0c88307115959db4bd5f2ec05d480435e226c332 100644
--- a/web/src/less/variable.less
+++ b/web/src/less/variable.less
@@ -15,5 +15,3 @@
 @fontSize14: 14px;
 @fontSize16: 16px;
 @fontSize18: 18px;
-
-@fontFamilyNunitoSans: 'Nunito Sans';
diff --git a/web/src/pages/add-knowledge/components/knowledge-file/model.ts b/web/src/pages/add-knowledge/components/knowledge-file/model.ts
index 18491ebe8d578ab7eca88dcfc1f4eb7510908113..a2a40d4d3fc53c3e178201722deb97cafe96a55b 100644
--- a/web/src/pages/add-knowledge/components/knowledge-file/model.ts
+++ b/web/src/pages/add-knowledge/components/knowledge-file/model.ts
@@ -4,7 +4,6 @@ import kbService, { getDocumentFile } from '@/services/kbService';
 import { message } from 'antd';
 import omit from 'lodash/omit';
 import pick from 'lodash/pick';
-import { Nullable } from 'typings';
 import { DvaModel } from 'umi';
 
 export interface KFModelState extends BaseState {
diff --git a/web/src/pages/add-knowledge/components/knowledge-file/parsing-action-cell/index.tsx b/web/src/pages/add-knowledge/components/knowledge-file/parsing-action-cell/index.tsx
index 186cc28c4155988e687bc6af960467ac4edeb447..49cb0b1464bc3d0aa362c53879dad236d314853d 100644
--- a/web/src/pages/add-knowledge/components/knowledge-file/parsing-action-cell/index.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-file/parsing-action-cell/index.tsx
@@ -1,10 +1,17 @@
 import showDeleteConfirm from '@/components/deleting-confirm';
 import { IKnowledgeFile } from '@/interfaces/database/knowledge';
-import { DeleteOutlined, EditOutlined, ToolOutlined } from '@ant-design/icons';
+import {
+  DeleteOutlined,
+  DownloadOutlined,
+  EditOutlined,
+  ToolOutlined,
+} from '@ant-design/icons';
 import { Button, Dropdown, MenuProps, Space, Tooltip } from 'antd';
 import { useDispatch } from 'umi';
 import { isParserRunning } from '../utils';
 
+import { api_host } from '@/utils/api';
+import { downloadFile } from '@/utils/fileUtil';
 import styles from './index.less';
 
 interface IProps {
@@ -38,6 +45,13 @@ const ParsingActionCell = ({
     }
   };
 
+  const onDownloadDocument = () => {
+    downloadFile({
+      url: `${api_host}/document/get/${documentId}`,
+      filename: record.name,
+    });
+  };
+
   const setCurrentRecord = () => {
     dispatch({
       type: 'kFModel/setCurrentRecord',
@@ -110,6 +124,14 @@ const ParsingActionCell = ({
       >
         <DeleteOutlined size={20} />
       </Button>
+      <Button
+        type="text"
+        disabled={isRunning}
+        onClick={onDownloadDocument}
+        className={styles.iconButton}
+      >
+        <DownloadOutlined size={20} />
+      </Button>
     </Space>
   );
 };
diff --git a/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx b/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
index 7d50cfbfbc05063f4f4865b2d36beebd022007e1..890c66213d8da95448170c560416e6673cfa8222 100644
--- a/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
@@ -91,6 +91,7 @@ const Configuration = () => {
           <Form.Item
             name="permission"
             label="Permissions"
+            tooltip="coming soon"
             rules={[{ required: true }]}
           >
             <Radio.Group>
diff --git a/web/src/pages/add-knowledge/components/knowledge-sidebar/index.less b/web/src/pages/add-knowledge/components/knowledge-sidebar/index.less
index 435e1261b21e8a724aa3bfc4465e5910a94d075f..735e10b6e8579a46d3a6966e032b2b46507a61be 100644
--- a/web/src/pages/add-knowledge/components/knowledge-sidebar/index.less
+++ b/web/src/pages/add-knowledge/components/knowledge-sidebar/index.less
@@ -8,7 +8,6 @@
     .knowledgeLogo {
     }
     .knowledgeTitle {
-      font-family: 'Nunito Sans';
       font-size: 16px;
       line-height: 24px;
       font-weight: @fontWeight700;
@@ -16,7 +15,6 @@
       margin-bottom: 6px;
     }
     .knowledgeDescription {
-      font-family: 'Nunito Sans';
       font-size: 12px;
       font-weight: @fontWeight600;
       color: @gray8;
@@ -55,7 +53,6 @@
 
     .menuText {
       color: @gray3;
-      font-family: @fontFamilyNunitoSans;
       font-size: @fontSize14;
       font-weight: @fontWeight700;
     }
diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx
index 637fd89fbacbe9434884bda6013b1f8843642fd6..81d9a956270c56f633325c9d16f4428081ad7a81 100644
--- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx
@@ -51,8 +51,12 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
             top_k: 1024,
           }}
         >
-          <SimilaritySlider></SimilaritySlider>
-          <Form.Item<FieldType> label="Top k" name={'top_k'}>
+          <SimilaritySlider isTooltipShown></SimilaritySlider>
+          <Form.Item<FieldType>
+            label="Top k"
+            name={'top_k'}
+            tooltip="coming soon"
+          >
             <Slider marks={{ 0: 0, 2048: 2048 }} max={2048} />
           </Form.Item>
           <Card size="small" title="Test text">
diff --git a/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx b/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx
index 329c121f8eeb0b3a705c9fc74906cd022c6972ca..15b670e80ecad0e5ebe49bc0ca90d5a7be9c36d2 100644
--- a/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx
+++ b/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx
@@ -50,7 +50,12 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
           </button>
         </Upload>
       </Form.Item>
-      <Form.Item name={'language'} label="Language" initialValue={'Chinese'}>
+      <Form.Item
+        name={'language'}
+        label="Language"
+        initialValue={'Chinese'}
+        tooltip="coming soon"
+      >
         <Select
           options={[
             { value: 'Chinese', label: 'Chinese' },
@@ -61,12 +66,14 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
       <Form.Item
         name={['prompt_config', 'empty_response']}
         label="Empty response"
+        tooltip="coming soon"
       >
         <Input placeholder="" />
       </Form.Item>
       <Form.Item
         name={['prompt_config', 'prologue']}
         label="Set an opener"
+        tooltip="coming soon"
         initialValue={"Hi! I'm your assistant, what can I do for you?"}
       >
         <Input.TextArea autoSize={{ minRows: 5 }} />
@@ -74,6 +81,7 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
       <Form.Item
         label="Select one context"
         name="kb_ids"
+        tooltip="coming soon"
         rules={[
           {
             required: true,
diff --git a/web/src/pages/chat/chat-configuration-modal/index.less b/web/src/pages/chat/chat-configuration-modal/index.less
index 5da722d8630cc37ab4c12af7113d67291d8e7aa1..706725ccfa02306faca7cf497acbebae70a3a402 100644
--- a/web/src/pages/chat/chat-configuration-modal/index.less
+++ b/web/src/pages/chat/chat-configuration-modal/index.less
@@ -5,11 +5,18 @@
 .variableContainer {
   padding-bottom: 20px;
   .variableAlign {
-    text-align: right;
+    text-align: end;
   }
 
   .variableLabel {
-    margin-right: 16px;
+    margin-right: 14px;
+  }
+
+  .variableIcon {
+    margin-inline-start: 4px;
+    color: rgba(0, 0, 0, 0.45);
+    cursor: help;
+    writing-mode: horizontal-tb;
   }
 
   .variableTable {
diff --git a/web/src/pages/chat/chat-configuration-modal/model-setting.tsx b/web/src/pages/chat/chat-configuration-modal/model-setting.tsx
index 44d6a82a96b76354cff329ba677d681e80e52c35..cf2ec45cdbbee372e4b8cbca6f3906634a331e8a 100644
--- a/web/src/pages/chat/chat-configuration-modal/model-setting.tsx
+++ b/web/src/pages/chat/chat-configuration-modal/model-setting.tsx
@@ -46,6 +46,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
       <Form.Item
         label="Model"
         name="llm_id"
+        tooltip="coming soon"
         rules={[{ required: true, message: 'Please select!' }]}
       >
         <Select options={modelOptions} showSearch />
@@ -54,6 +55,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
       <Form.Item
         label="Parameters"
         name="parameters"
+        tooltip="coming soon"
         initialValue={ModelVariableType.Precise}
         // rules={[{ required: true, message: 'Please input!' }]}
       >
diff --git a/web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx b/web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx
index b8e46d32bb82fa43c44411835aab285a9beeb1d1..758330ad51ac6f673e9047228c935fd953af325e 100644
--- a/web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx
+++ b/web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx
@@ -1,5 +1,5 @@
 import SimilaritySlider from '@/components/similarity-slider';
-import { DeleteOutlined } from '@ant-design/icons';
+import { DeleteOutlined, QuestionCircleOutlined } from '@ant-design/icons';
 import {
   Button,
   Col,
@@ -11,6 +11,7 @@ import {
   Switch,
   Table,
   TableProps,
+  Tooltip,
 } from 'antd';
 import classNames from 'classnames';
 import {
@@ -153,6 +154,7 @@ const PromptEngine = (
       <Form.Item
         label="System"
         rules={[{ required: true, message: 'Please input!' }]}
+        tooltip="coming soon"
         name={['prompt_config', 'system']}
         initialValue={`你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。
         以下是知识库:
@@ -173,10 +175,15 @@ const PromptEngine = (
       </Form.Item>
       <section className={classNames(styles.variableContainer)}>
         <Row align={'middle'} justify="end">
-          <Col span={6} className={styles.variableAlign}>
-            <label className={styles.variableLabel}>Variables</label>
+          <Col span={7} className={styles.variableAlign}>
+            <label className={styles.variableLabel}>
+              Variables
+              <Tooltip title="coming soon">
+                <QuestionCircleOutlined className={styles.variableIcon} />
+              </Tooltip>
+            </label>
           </Col>
-          <Col span={18} className={styles.variableAlign}>
+          <Col span={17} className={styles.variableAlign}>
             <Button size="small" onClick={handleAdd}>
               Add
             </Button>
@@ -184,8 +191,8 @@ const PromptEngine = (
         </Row>
         {dataSource.length > 0 && (
           <Row>
-            <Col span={6}></Col>
-            <Col span={18}>
+            <Col span={7}> </Col>
+            <Col span={17}>
               <Table
                 dataSource={dataSource}
                 columns={columns}
diff --git a/web/src/pages/chat/chat-container/index.tsx b/web/src/pages/chat/chat-container/index.tsx
index 17a94699be70cb811a27aee5cc3c3b3eb0706b57..bb36839e3d26e8b963798d46a1ba1d8340b01f14 100644
--- a/web/src/pages/chat/chat-container/index.tsx
+++ b/web/src/pages/chat/chat-container/index.tsx
@@ -18,6 +18,7 @@ import {
   Popover,
   Skeleton,
   Space,
+  Spin,
 } from 'antd';
 import classNames from 'classnames';
 import { useCallback, useMemo } from 'react';
@@ -31,6 +32,7 @@ import {
   useFetchConversationOnMount,
   useGetFileIcon,
   useGetSendButtonDisabled,
+  useSelectConversationLoading,
   useSendMessage,
 } from '../hooks';
 
@@ -259,29 +261,32 @@ const ChatContainer = () => {
     useClickDrawer();
   const disabled = useGetSendButtonDisabled();
   useGetFileIcon();
+  const loading = useSelectConversationLoading();
 
   return (
     <>
       <Flex flex={1} className={styles.chatContainer} vertical>
         <Flex flex={1} vertical className={styles.messageContainer}>
           <div>
-            {conversation?.message?.map((message) => {
-              const assistantMessages = conversation?.message
-                ?.filter((x) => x.role === MessageType.Assistant)
-                .slice(1);
-              const referenceIndex = assistantMessages.findIndex(
-                (x) => x.id === message.id,
-              );
-              const reference = conversation.reference[referenceIndex];
-              return (
-                <MessageItem
-                  key={message.id}
-                  item={message}
-                  reference={reference}
-                  clickDocumentButton={clickDocumentButton}
-                ></MessageItem>
-              );
-            })}
+            <Spin spinning={loading}>
+              {conversation?.message?.map((message) => {
+                const assistantMessages = conversation?.message
+                  ?.filter((x) => x.role === MessageType.Assistant)
+                  .slice(1);
+                const referenceIndex = assistantMessages.findIndex(
+                  (x) => x.id === message.id,
+                );
+                const reference = conversation.reference[referenceIndex];
+                return (
+                  <MessageItem
+                    key={message.id}
+                    item={message}
+                    reference={reference}
+                    clickDocumentButton={clickDocumentButton}
+                  ></MessageItem>
+                );
+              })}
+            </Spin>
           </div>
           <div ref={ref} />
         </Flex>
diff --git a/web/src/pages/chat/hooks.ts b/web/src/pages/chat/hooks.ts
index 6da74c46ff9d473419c28f68d4e4d32fd4964a3c..725d8aa30a4bc848c0bee7f620ad3c995757e89b 100644
--- a/web/src/pages/chat/hooks.ts
+++ b/web/src/pages/chat/hooks.ts
@@ -773,6 +773,9 @@ export const useSelectDialogListLoading = () => {
 export const useSelectConversationListLoading = () => {
   return useOneNamespaceEffectsLoading('chatModel', ['listConversation']);
 };
+export const useSelectConversationLoading = () => {
+  return useOneNamespaceEffectsLoading('chatModel', ['getConversation']);
+};
 
 export const useGetSendButtonDisabled = () => {
   const { dialogId, conversationId } = useGetChatSearchParams();
diff --git a/web/src/pages/knowledge/knowledge-card/index.less b/web/src/pages/knowledge/knowledge-card/index.less
index 5c37caa53b1dd00e9f44e4b7ce2d00562cd16a5e..1c7d174e0fbf3c597aa6da8044dd2881fd2ff914 100644
--- a/web/src/pages/knowledge/knowledge-card/index.less
+++ b/web/src/pages/knowledge/knowledge-card/index.less
@@ -40,6 +40,7 @@
       line-height: 32px;
       font-weight: 600;
       color: rgba(0, 0, 0, 0.88);
+      word-break: break-all;
     }
     .description {
       font-size: 12px;
diff --git a/web/src/pages/login/index.less b/web/src/pages/login/index.less
index 46838489ef1c532e89243cc2227fedea888579ac..82f3c20cca76a330ce6d55e8260607ae429014e2 100644
--- a/web/src/pages/login/index.less
+++ b/web/src/pages/login/index.less
@@ -4,7 +4,6 @@
   // width: 100%;
   display: flex;
 
-
   .loginLeft {
     // width: 610px;
     width: 40%;
@@ -12,9 +11,7 @@
     height: 100vh;
   }
 
-
-
-  @media screen and (max-width:957px) {
+  @media screen and (max-width: 957px) {
     .loginLeft {
       // width: 610px;
       width: 100%;
@@ -29,13 +26,11 @@
 
   .loginRight {
     flex: 1;
-    background-color: #F2F4F7;
-    ;
+    background-color: #f2f4f7;
   }
 
   .loginTitle {
     //styleName: Heading/1;
-    font-family: SF Pro Text;
     font-size: 38px;
     font-weight: 600;
     line-height: 46px;
@@ -48,9 +43,8 @@
       font-size: 16px;
       line-height: 24px;
 
-      color: #000000A6;
+      color: #000000a6;
     }
-
   }
 }
 
@@ -59,5 +53,5 @@
   width: 60%;
   height: 724px;
   padding: 5px, 0px, 5px, 0px;
-  margin: 80px auto
-}
\ No newline at end of file
+  margin: 80px auto;
+}
diff --git a/web/src/pages/setting/model.ts b/web/src/pages/setting/model.ts
index 7599f5df39c300373ab643d1eb174057feda47e0..6e628ded8e75182b02edea2516fb6410b0546c53 100644
--- a/web/src/pages/setting/model.ts
+++ b/web/src/pages/setting/model.ts
@@ -7,7 +7,6 @@ import {
 import { IUserInfo } from '@/interfaces/database/userSetting';
 import userService from '@/services/userService';
 import { message } from 'antd';
-import { Nullable } from 'typings';
 import { DvaModel } from 'umi';
 
 export interface SettingModelState {
diff --git a/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx b/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx
index 3829cef99222d21b50def8a3f8160ba1940334ec..fabdec4bee58887d4e614db742f9e3ca2700d0f0 100644
--- a/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx
+++ b/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx
@@ -66,6 +66,7 @@ const ApiKeyModal = ({
         <Form.Item<FieldType>
           label="Api-Key"
           name="api_key"
+          tooltip="coming soon"
           rules={[{ required: true, message: 'Please input api key!' }]}
         >
           <Input />
diff --git a/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx b/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx
index 8bfa216553706860f1d6a1bcabe7cb5df138fab1..54ccc72b2aeac145c63c9bd7228b63f2c39735d0 100644
--- a/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx
+++ b/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx
@@ -43,16 +43,24 @@ const SystemModelSettingModal = ({
       confirmLoading={loading}
     >
       <Form form={form} onValuesChange={onFormLayoutChange} layout={'vertical'}>
-        <Form.Item label="Sequence2txt model" name="asr_id">
+        <Form.Item
+          label="Sequence2txt model"
+          name="asr_id"
+          tooltip="coming soon"
+        >
           <Select options={allOptions[LlmModelType.Speech2text]} />
         </Form.Item>
-        <Form.Item label="Embedding model" name="embd_id">
+        <Form.Item label="Embedding model" name="embd_id" tooltip="coming soon">
           <Select options={allOptions[LlmModelType.Embedding]} />
         </Form.Item>
-        <Form.Item label="Img2txt model" name="img2txt_id">
+        <Form.Item
+          label="Img2txt model"
+          name="img2txt_id"
+          tooltip="coming soon"
+        >
           <Select options={allOptions[LlmModelType.Image2text]} />
         </Form.Item>
-        <Form.Item label="Chat model" name="llm_id">
+        <Form.Item label="Chat model" name="llm_id" tooltip="coming soon">
           <Select options={allOptions[LlmModelType.Chat]} />
         </Form.Item>
       </Form>
diff --git a/web/src/pages/user-setting/setting-profile/index.tsx b/web/src/pages/user-setting/setting-profile/index.tsx
index 64f48a24b67965811b646e7e9a3cf20664a77de7..13ea15f7aa85eca934081435ea1329b7cef1ebe4 100644
--- a/web/src/pages/user-setting/setting-profile/index.tsx
+++ b/web/src/pages/user-setting/setting-profile/index.tsx
@@ -110,7 +110,7 @@ const UserSettingProfile = () => {
               <div>
                 <Space>
                   Your photo
-                  <Tooltip title="prompt text">
+                  <Tooltip title="coming soon">
                     <QuestionCircleOutlined />
                   </Tooltip>
                 </Space>
@@ -140,6 +140,7 @@ const UserSettingProfile = () => {
           <Form.Item<FieldType>
             label="Color schema"
             name="color_schema"
+            tooltip="coming soon"
             rules={[
               { required: true, message: 'Please select your color schema!' },
             ]}
@@ -153,6 +154,7 @@ const UserSettingProfile = () => {
           <Form.Item<FieldType>
             label="Language"
             name="language"
+            tooltip="coming soon"
             rules={[{ required: true, message: 'Please input your language!' }]}
           >
             <Select placeholder="select your language">
@@ -164,6 +166,7 @@ const UserSettingProfile = () => {
           <Form.Item<FieldType>
             label="Timezone"
             name="timezone"
+            tooltip="coming soon"
             rules={[{ required: true, message: 'Please input your timezone!' }]}
           >
             <Select placeholder="select your timezone" showSearch>
diff --git a/web/src/utils/fileUtil.ts b/web/src/utils/fileUtil.ts
index 40b6bd9f71f9a9f8e641170e350b4b31ded7fb85..3ec01f3ff69140200c70e5633fda675a889c395c 100644
--- a/web/src/utils/fileUtil.ts
+++ b/web/src/utils/fileUtil.ts
@@ -61,3 +61,27 @@ export const getBase64FromUploadFileList = async (fileList?: UploadFile[]) => {
 
   return '';
 };
+
+export const downloadFile = ({
+  url,
+  filename,
+  target,
+}: {
+  url: string;
+  filename?: string;
+  target?: string;
+}) => {
+  const downloadElement = document.createElement('a');
+  downloadElement.style.display = 'none';
+  downloadElement.href = url;
+  if (target) {
+    downloadElement.target = '_blank';
+  }
+  downloadElement.rel = 'noopener noreferrer';
+  if (filename) {
+    downloadElement.download = filename;
+  }
+  document.body.appendChild(downloadElement);
+  downloadElement.click();
+  document.body.removeChild(downloadElement);
+};
diff --git a/web/typings.d.ts b/web/typings.d.ts
index 632c633ad340dbd62a315afec44ed03d2f087826..cbe06bea1f4020f78d98729b3a84c01ebbb25219 100644
--- a/web/typings.d.ts
+++ b/web/typings.d.ts
@@ -1,4 +1,8 @@
 import 'umi/typings';
 declare module 'lodash';
 
-export type Nullable<T> = T | null;
+// declare type Nullable<T> = T | null; invalid
+
+declare global {
+  type Nullable<T> = T | null;
+}