From 1fc52033ba28805821e17956cd53c5998daba425 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 30 Apr 2025 13:09:42 +0800 Subject: [PATCH] Feat: Using IconFont as an additional icon library #3221 (#7427) ### What problem does this PR solve? Feat: Using IconFont as an additional icon library #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/public/iconfont.js | 70 +++++++++++++++++++++++++ web/src/components/icon-font.tsx | 12 +++++ web/src/components/more-button.tsx | 20 +++++++ web/src/locales/en.ts | 4 +- web/src/locales/zh-traditional.ts | 4 +- web/src/locales/zh.ts | 4 +- web/src/pages/datasets/dataset-card.tsx | 14 ++--- web/src/pages/home/application-card.tsx | 27 ++++++---- web/src/pages/home/applications.tsx | 62 ++++++---------------- web/src/pages/home/banner.tsx | 4 +- web/src/pages/home/datasets.tsx | 8 ++- 11 files changed, 158 insertions(+), 71 deletions(-) create mode 100644 web/public/iconfont.js create mode 100644 web/src/components/icon-font.tsx create mode 100644 web/src/components/more-button.tsx diff --git a/web/public/iconfont.js b/web/public/iconfont.js new file mode 100644 index 000000000..38ce5d710 --- /dev/null +++ b/web/public/iconfont.js @@ -0,0 +1,70 @@ +(window._iconfont_svg_string_4909832 = + ''), + ((l) => { + var a = (h = (h = document.getElementsByTagName('script'))[ + h.length - 1 + ]).getAttribute('data-injectcss'), + h = h.getAttribute('data-disable-injectsvg'); + if (!h) { + var t, + c, + i, + v, + p, + z = function (a, h) { + h.parentNode.insertBefore(a, h); + }; + if (a && !l.__iconfont__svg__cssinject__) { + l.__iconfont__svg__cssinject__ = !0; + try { + document.write( + '', + ); + } catch (a) { + console && console.log(a); + } + } + (t = function () { + var a, + h = document.createElement('div'); + (h.innerHTML = l._iconfont_svg_string_4909832), + (h = h.getElementsByTagName('svg')[0]) && + (h.setAttribute('aria-hidden', 'true'), + (h.style.position = 'absolute'), + (h.style.width = 0), + (h.style.height = 0), + (h.style.overflow = 'hidden'), + (h = h), + (a = document.body).firstChild + ? z(h, a.firstChild) + : a.appendChild(h)); + }), + document.addEventListener + ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState) + ? setTimeout(t, 0) + : ((c = function () { + document.removeEventListener('DOMContentLoaded', c, !1), t(); + }), + document.addEventListener('DOMContentLoaded', c, !1)) + : document.attachEvent && + ((i = t), + (v = l.document), + (p = !1), + e(), + (v.onreadystatechange = function () { + 'complete' == v.readyState && + ((v.onreadystatechange = null), d()); + })); + } + function d() { + p || ((p = !0), i()); + } + function e() { + try { + v.documentElement.doScroll('left'); + } catch (a) { + return void setTimeout(e, 50); + } + d(); + } + })(window); diff --git a/web/src/components/icon-font.tsx b/web/src/components/icon-font.tsx new file mode 100644 index 000000000..6154165b1 --- /dev/null +++ b/web/src/components/icon-font.tsx @@ -0,0 +1,12 @@ +import { cn } from '@/lib/utils'; + +type IconFontType = { + name: string; + className?: string; +}; + +export const IconFont = ({ name, className }: IconFontType) => ( + + + +); diff --git a/web/src/components/more-button.tsx b/web/src/components/more-button.tsx new file mode 100644 index 000000000..f8d2d75dc --- /dev/null +++ b/web/src/components/more-button.tsx @@ -0,0 +1,20 @@ +import { cn } from '@/lib/utils'; +import { Ellipsis } from 'lucide-react'; +import React from 'react'; +import { Button, ButtonProps } from './ui/button'; + +export const MoreButton = React.forwardRef( + ({ className, size, ...props }, ref) => { + return ( + + ); + }, +); diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index dc7cf5e27..05408d55d 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -71,6 +71,7 @@ export default { fileManager: 'File Management', flow: 'Agent', search: 'Search', + welcome: 'Welcome to', }, knowledgeList: { welcome: 'Welcome back', @@ -337,7 +338,8 @@ The above is the content you need to summarize.`, maxTokenTip: 'The maximum number of tokens per generated summary chunk.', maxTokenMessage: 'Max token is required', threshold: 'Threshold', - thresholdTip: 'In RAPTOR, chunks are clustered by their semantic similarity. The Threshold parameter sets the minimum similarity required for chunks to be grouped together. A higher Threshold means fewer chunks in each cluster, while a lower one means more.', + thresholdTip: + 'In RAPTOR, chunks are clustered by their semantic similarity. The Threshold parameter sets the minimum similarity required for chunks to be grouped together. A higher Threshold means fewer chunks in each cluster, while a lower one means more.', thresholdMessage: 'Threshold is required', maxCluster: 'Max cluster', maxClusterTip: 'The maximum number of clusters to create.', diff --git a/web/src/locales/zh-traditional.ts b/web/src/locales/zh-traditional.ts index 74fe18f95..555c6387b 100644 --- a/web/src/locales/zh-traditional.ts +++ b/web/src/locales/zh-traditional.ts @@ -71,6 +71,7 @@ export default { fileManager: '文件管理', flow: 'Agent', search: '搜尋', + welcome: '歡迎來到', }, knowledgeList: { welcome: '歡迎回來', @@ -332,7 +333,8 @@ export default { promptTip: '系統提示為大型模型提供任務描述、規定回覆方式,以及設定其他各種要求。系統提示通常與 key(變數)合用,透過變數設定大型模型的輸入資料。你可以透過斜線或 (x) 按鈕顯示可用的 key。', maxTokenTip: '用於設定每個被總結的文字塊的最大 token 數。', - thresholdTip: '在 RAPTOR 中,數據塊會根據它們的語義相似性進行聚類。閾值參數設定了數據塊被分到同一組所需的最小相似度。閾值越高,每個聚類中的數據塊越少;閾值越低,則每個聚類中的數據塊越多。', + thresholdTip: + '在 RAPTOR 中,數據塊會根據它們的語義相似性進行聚類。閾值參數設定了數據塊被分到同一組所需的最小相似度。閾值越高,每個聚類中的數據塊越少;閾值越低,則每個聚類中的數據塊越多。', maxClusterTip: '最多可創建的聚類數。', entityTypes: '實體類型', pageRank: '頁面排名', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 106ec9265..1b16a7abd 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -71,6 +71,7 @@ export default { fileManager: '文件管理', flow: 'Agent', search: '搜索', + welcome: '欢迎来到', }, knowledgeList: { welcome: '欢迎回来', @@ -349,7 +350,8 @@ export default { promptTip: '系统提示为大模型提供任务描述、规定回复方式,以及设置其他各种要求。系统提示通常与 key (变量)合用,通过变量设置大模型的输入数据。你可以通过斜杠或者 (x) 按钮显示可用的 key。', maxTokenTip: '用于设定每个被总结的文本块的最大 token 数。', - thresholdTip: '在 RAPTOR 中,数据块会根据它们的语义相似性进行聚类。阈值设定了数据块被分到同一组所需的最小相似度。阈值越高,每个聚类中的数据块越少;阈值越低,则每个聚类中的数据块越多。', + thresholdTip: + '在 RAPTOR 中,数据块会根据它们的语义相似性进行聚类。阈值设定了数据块被分到同一组所需的最小相似度。阈值越高,每个聚类中的数据块越少;阈值越低,则每个聚类中的数据块越多。', maxClusterTip: '最多可创建的聚类数。', entityTypes: '实体类型', pageRank: '页面排名', diff --git a/web/src/pages/datasets/dataset-card.tsx b/web/src/pages/datasets/dataset-card.tsx index e4fd13cd1..222d73fb3 100644 --- a/web/src/pages/datasets/dataset-card.tsx +++ b/web/src/pages/datasets/dataset-card.tsx @@ -1,11 +1,11 @@ +import { MoreButton } from '@/components/more-button'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Badge } from '@/components/ui/badge'; -import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { IKnowledge } from '@/interfaces/database/knowledge'; import { formatDate } from '@/utils/date'; -import { ChevronRight, Ellipsis } from 'lucide-react'; +import { ChevronRight } from 'lucide-react'; import { DatasetDropdown } from './dataset-dropdown'; import { useDisplayOwnerName } from './use-display-owner'; import { useRenameDataset } from './use-rename-dataset'; @@ -29,7 +29,7 @@ export function DatasetCard({ className="w-40" onClick={navigateToDataset(dataset.id)} > - +
@@ -46,13 +46,7 @@ export function DatasetCard({ showDatasetRenameModal={showDatasetRenameModal} dataset={dataset} > - +
diff --git a/web/src/pages/home/application-card.tsx b/web/src/pages/home/application-card.tsx index c3d9cb6fb..6a6003f57 100644 --- a/web/src/pages/home/application-card.tsx +++ b/web/src/pages/home/application-card.tsx @@ -1,3 +1,4 @@ +import { MoreButton } from '@/components/more-button'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Card, CardContent } from '@/components/ui/card'; import { formatDate } from '@/utils/date'; @@ -14,17 +15,23 @@ type ApplicationCardProps = { export function ApplicationCard({ app }: ApplicationCardProps) { return ( - - - - CN - -
-

{app.title}

-

- {formatDate(app.update_time)} -

+ +
+ + + CN + +
+

+ {app.title} +

+

+ {formatDate(app.update_time)} +

+
+ +
); diff --git a/web/src/pages/home/applications.tsx b/web/src/pages/home/applications.tsx index 29e8291b6..60ccba714 100644 --- a/web/src/pages/home/applications.tsx +++ b/web/src/pages/home/applications.tsx @@ -1,48 +1,21 @@ +import { IconFont } from '@/components/icon-font'; import { Segmented, SegmentedValue } from '@/components/ui/segmented'; import { Routes } from '@/routes'; -import { Cpu, MessageSquare, Search } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'umi'; import { Agents } from './agent-list'; -import { ApplicationCard, SeeAllAppCard } from './application-card'; +import { SeeAllAppCard } from './application-card'; import { ChatList } from './chat-list'; -const applications = [ - { - id: 1, - title: 'Jarvis chatbot', - type: 'Chat app', - update_time: '11/24/2024', - avatar: , - }, - { - id: 2, - title: 'Search app 01', - type: 'Search app', - update_time: '11/24/2024', - avatar: , - }, - { - id: 3, - title: 'Chatbot 01', - type: 'Chat app', - update_time: '11/24/2024', - avatar: , - }, - { - id: 4, - title: 'Workflow 01', - type: 'Agent', - update_time: '11/24/2024', - avatar: , - }, -]; - -const All = 'all'; +const IconMap = { + [Routes.Chats]: 'chat', + [Routes.Searches]: 'search', + [Routes.Agents]: 'agent', +}; export function Applications() { - const [val, setVal] = useState('all'); + const [val, setVal] = useState(Routes.Chats); const { t } = useTranslation(); const navigate = useNavigate(); @@ -52,10 +25,6 @@ export function Applications() { const options = useMemo( () => [ - { - label: 'All', - value: All, - }, { value: Routes.Chats, label: t('header.chat') }, { value: Routes.Searches, label: t('header.search') }, { value: Routes.Agents, label: t('header.flow') }, @@ -70,7 +39,13 @@ export function Applications() { return (
-

Applications

+

+ + {options.find((x) => x.value === val)?.label} +

- {(val === All || val === Routes.Searches) && - [...Array(12)].map((_, i) => { - const app = applications[i % 4]; - return ; - })} {val === Routes.Agents && } {val === Routes.Chats && } - {val === All || } + {}
); diff --git a/web/src/pages/home/banner.tsx b/web/src/pages/home/banner.tsx index 84a784cef..284ceae94 100644 --- a/web/src/pages/home/banner.tsx +++ b/web/src/pages/home/banner.tsx @@ -1,5 +1,6 @@ import { Card, CardContent } from '@/components/ui/card'; import { ArrowRight, X } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; function BannerCard() { return ( @@ -39,9 +40,10 @@ export function Banner() { } export function NextBanner() { + const { t } = useTranslation(); return (
- Welcome to + {t('header.welcome')} RAGFlow diff --git a/web/src/pages/home/datasets.tsx b/web/src/pages/home/datasets.tsx index 183ea903f..85fdf848c 100644 --- a/web/src/pages/home/datasets.tsx +++ b/web/src/pages/home/datasets.tsx @@ -1,10 +1,13 @@ +import { IconFont } from '@/components/icon-font'; import { RenameDialog } from '@/components/rename-dialog'; import { CardSkeleton } from '@/components/ui/skeleton'; import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request'; +import { useTranslation } from 'react-i18next'; import { DatasetCard, SeeAllCard } from '../datasets/dataset-card'; import { useRenameDataset } from '../datasets/use-rename-dataset'; export function Datasets() { + const { t } = useTranslation(); const { kbs, loading } = useFetchNextKnowledgeListByPage(); const { datasetRenameLoading, @@ -17,7 +20,10 @@ export function Datasets() { return (
-

Datasets

+

+ + {t('header.knowledgeBase')} +

{loading ? (