feat(web): ui

This commit is contained in:
zhaoying
2026-04-22 14:16:44 +08:00
parent 749083bdbe
commit cda20ac3f1
7 changed files with 68 additions and 51 deletions

View File

@@ -89,12 +89,15 @@ const Knowledge: FC<KnowledgeProps> = ({ value = { knowledge_bases: [] }, onChan
onChange?.({ ...editConfig, knowledge_bases: [...list] })
} else if (type === 'rerankerConfig') {
const rerankerValues = values as RerankerConfig
setEditConfig(prev => ({ ...prev, ...rerankerValues }))
onChange?.({
...editConfig,
...rerankerValues,
reranker_id: rerankerValues.rerank_model ? rerankerValues.reranker_id : undefined,
reranker_top_k: rerankerValues.rerank_model ? rerankerValues.reranker_top_k : undefined,
setEditConfig(prev => {
const next = {
...prev,
...rerankerValues,
reranker_id: rerankerValues.rerank_model ? rerankerValues.reranker_id : undefined,
reranker_top_k: rerankerValues.rerank_model ? rerankerValues.reranker_top_k : undefined,
}
onChange?.(next)
return next
})
}
}

View File

@@ -1,11 +1,11 @@
import { type FC } from 'react';
import { type FC, type MouseEvent } from 'react';
import { Dropdown } from 'antd';
import type { MenuProps } from 'antd';
interface MoreDropdownProps {
items: NonNullable<MenuProps['items']>;
placement?: 'bottomRight' | 'bottomLeft' | 'topRight' | 'topLeft';
onClick?: (e: React.MouseEvent) => void;
onClick?: (e: MouseEvent) => void;
}
/**

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:21:14
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-20 20:24:43
* @Last Modified time: 2026-04-22 12:03:08
*/
/**
* RbCard Component
@@ -67,7 +67,7 @@ const RbCard: FC<RbCardProps> = ({
{title}
</div>
</Tooltip>
: <div className="rb:flex-1 rb:leading-5.5 rb:min-w-0 rb:whitespace-break-spaces rb:wrap-break-word rb:line-clamp-2">
: <div className={`rb:flex-1 rb:leading-5.5 rb:min-w-0 rb:whitespace-break-spaces rb:wrap-break-word rb:line-clamp-2 ${titleClassName}`}>
{title}
</div>
}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:29:57
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-22 11:39:15
* @Last Modified time: 2026-04-22 13:48:09
*/
/**
* Tag Component
@@ -40,7 +40,7 @@ const colors = {
/** Custom tag component with color themes */
const Tag: FC<TagProps> = ({ color = 'processing', children, className, variant = 'outline' }) => {
return (
<span className={`rb:inline-block rb:px-1 rb:py-0.5 rb:rounded-sm rb:text-[12px] rb:font-regular! rb:leading-4 rb:border ${colors[color]} ${className || ''}, ${variant === 'borderless' ? 'rb:border-none!' : ''}`}>
<span className={`rb:inline-block rb:px-1 rb:py-0.5 rb:rounded-sm rb:text-[12px] rb:font-regular! rb:leading-4 rb:border ${colors[color]} ${className || ''} ${variant === 'borderless' ? 'rb:border-none!' : ''}`}>
{children}
</span>
)

View File

@@ -11,6 +11,15 @@ import App from '@/App.tsx'
// Synchronously import i18n config to ensure initialization before component rendering
import './i18n'
// Fix autofill background color on focus
document.addEventListener('animationstart', (e) => {
if (e.animationName === 'onAutoFillStart') {
const input = e.target as HTMLInputElement
input.style.backgroundColor = 'transparent'
input.addEventListener('focus', () => { input.style.backgroundColor = 'transparent' }, { once: false })
}
})
// After a new release, old dynamic chunk files are deleted; force a page reload on preload error
window.addEventListener('vite:preloadError', () => {
console.warn('New version detected, reloading page to load latest assets...')

View File

@@ -457,4 +457,14 @@ body {
.pageTabs.ant-segmented .ant-segmented-item-selected {
box-shadow: 0px 2px 4px 0px rgba(33, 35, 50, 0.16);
}
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px transparent inset !important;
transition: background-color 5000s ease-in-out 0s !important;
animation-name: onAutoFillStart;
animation-duration: 1ms;
}
@keyframes onAutoFillStart { from {} to {} }

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 15:52:50
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-22 11:34:06
* @Last Modified time: 2026-04-22 12:07:40
*/
import React, { useRef } from 'react';
import { useTranslation } from 'react-i18next';
@@ -95,44 +95,39 @@ const ApiKeyManagement: React.FC = () => {
renderItem={(apiKeyItem) => {
return (
<RbCard
title={
<Flex justify="space-between" className="rb:w-full!">
<Flex gap={4} vertical className="rb:flex-1!">
{apiKeyItem.name}
<Flex gap={6}>
{apiKeyItem.scopes?.includes('memory') && <Tag>{t('apiKey.memoryEngine')}</Tag>}
{apiKeyItem.scopes?.includes('rag') && <Tag color="success">{t('apiKey.knowledgeBase')}</Tag>}
{!apiKeyItem.scopes?.includes('memory') && !apiKeyItem.scopes?.includes('rag') && <div className="rb:font-regular!">{t('apiKey.noScopes')}</div>}
</Flex>
</Flex>
<MoreDropdown
items={[
{
key: 'edit',
icon: <div className="rb:size-4 rb:bg-cover rb:cursor-pointer rb:bg-[url('@/assets/images/common/edit_bold.svg')]" />,
label: t('common.edit'),
onClick: () => handleEdit(apiKeyItem),
},
{
key: 'view',
icon: <div className="rb:size-4 rb:bg-cover rb:cursor-pointer rb:bg-[url('@/assets/images/common/eye.svg')]" />,
label: t('common.view'),
onClick: () => handleView(apiKeyItem),
},
{
key: 'delete',
danger: true,
icon: <div className="rb:size-4 rb:bg-cover rb:cursor-pointer rb:bg-[url('@/assets/images/common/delete_red_big.svg')]" />,
label: t('common.delete'),
onClick: () => handleDelete(apiKeyItem),
},
]}
/>
</Flex>
}
isNeedTooltip={false}
headerClassName="rb:min-h-[78px]!"
title={apiKeyItem.name}
extra={<MoreDropdown
items={[
{
key: 'edit',
icon: <div className="rb:size-4 rb:bg-cover rb:cursor-pointer rb:bg-[url('@/assets/images/common/edit_bold.svg')]" />,
label: t('common.edit'),
onClick: () => handleEdit(apiKeyItem),
},
{
key: 'view',
icon: <div className="rb:size-4 rb:bg-cover rb:cursor-pointer rb:bg-[url('@/assets/images/common/eye.svg')]" />,
label: t('common.view'),
onClick: () => handleView(apiKeyItem),
},
{
key: 'delete',
danger: true,
icon: <div className="rb:size-4 rb:bg-cover rb:cursor-pointer rb:bg-[url('@/assets/images/common/delete_red_big.svg')]" />,
label: t('common.delete'),
onClick: () => handleDelete(apiKeyItem),
},
]}
/>}
variant="borderless"
headerClassName="rb:min-h-[42px]!"
titleClassName="rb:line-clamp-1!"
>
<Flex gap={6} className="rb:-mt-2! rb:mb-4!">
{apiKeyItem.scopes?.includes('memory') && <Tag>{t('apiKey.memoryEngine')}</Tag>}
{apiKeyItem.scopes?.includes('rag') && <Tag color="success">{t('apiKey.knowledgeBase')}</Tag>}
{!apiKeyItem.scopes?.includes('memory') && !apiKeyItem.scopes?.includes('rag') && <div className="rb:font-regular!">{t('apiKey.noScopes')}</div>}
</Flex>
<RbDescriptions
items={['id', 'is_expired', 'created_at'].map(key => ({
key,