diff --git a/config/theme.ts b/config/theme.ts index ee6229c7..e7101de9 100644 --- a/config/theme.ts +++ b/config/theme.ts @@ -14,7 +14,10 @@ export default { Menu: { iconSize: 16, iconMarginInlineEnd: 12, - itemHeight: 44 + itemHeight: 44, + // itemColor: 'rgba(0,0,0,.88)', + itemHoverBg: 'rgba(0,0,0,0.04)', + itemActiveBg: 'rgba(0,0,0,0.04)' }, Progress: { lineBorderRadius: 4 diff --git a/src/components/logs-viewer/index.tsx b/src/components/logs-viewer/index.tsx index 29fe9c78..f841453f 100644 --- a/src/components/logs-viewer/index.tsx +++ b/src/components/logs-viewer/index.tsx @@ -3,7 +3,7 @@ import useContainerScroll from '@/hooks/use-container-scorll'; import Convert from 'ansi-to-html'; import classNames from 'classnames'; import hasAnsi from 'has-ansi'; -import { useEffect, useRef, useState } from 'react'; +import { memo, useEffect, useRef, useState } from 'react'; import './index.less'; interface LogsViewerProps { @@ -75,4 +75,4 @@ const LogsViewer: React.FC = (props) => { ); }; -export default LogsViewer; +export default memo(LogsViewer); diff --git a/src/components/seal-form/seal-input.tsx b/src/components/seal-form/seal-input.tsx index 25105e6a..f6236008 100644 --- a/src/components/seal-form/seal-input.tsx +++ b/src/components/seal-form/seal-input.tsx @@ -17,6 +17,7 @@ const SealInput: React.FC = (props) => { isInFormItems = true, variant, addAfter, + trim = true, ...rest } = props; const [isFocus, setIsFocus] = useState(false); @@ -43,7 +44,9 @@ const SealInput: React.FC = (props) => { }; const handleChange = (e: any) => { - e.target.value = e.target.value?.trim?.(); + if (trim) { + e.target.value = e.target.value?.trim?.(); + } props.onChange?.(e); }; diff --git a/src/components/seal-form/types.ts b/src/components/seal-form/types.ts index d6ad2ad0..0e45df92 100644 --- a/src/components/seal-form/types.ts +++ b/src/components/seal-form/types.ts @@ -8,4 +8,5 @@ export interface SealFormItemProps { extra?: React.ReactNode; addAfter?: React.ReactNode; loading?: React.ReactNode; + trim?: boolean; } diff --git a/src/global.less b/src/global.less index 29f06574..135125db 100644 --- a/src/global.less +++ b/src/global.less @@ -49,6 +49,7 @@ html { --ant-input-active-border-color: #007bff; --ant-input-hover-border-color: #2997ff; --box-shadow-base: 0 4px 6px rgba(227, 232, 240, 70%); + --ant-border-radius-lg: 8px; // --box-shadow-base: none; .css-var-rf { @@ -64,12 +65,14 @@ html { // --ant-menu-item-selected-bg: var(--color-white-1); --ant-menu-item-border-radius: 8px; --ant-menu-item-selected-color: var(--ant-color-primary); - --ant-menu-item-hover-bg: var(--color-white-1); --ant-menu-item-color: var(--color-text-1); - --ant-menu-item-active-bg: var(--color-white-1); } } + .css-var-rj.ant-menu-css-var { + --ant-menu-item-color: var(--color-text-1); + } + .css-var-r0 { // --ant-control-height: 36px; --ant-font-size-sm: var(--font-size-base); @@ -109,12 +112,10 @@ html { .css-var-ri.ant-menu-css-var, .css-var-rh.ant-menu-css-var { --ant-menu-item-height: 46px; - // --ant-menu-item-selected-bg: var(--color-white-1); --ant-menu-item-border-radius: 8px; --ant-menu-item-selected-color: var(--ant-color-primary); - --ant-menu-item-hover-bg: var(--color-white-1); --ant-menu-item-color: var(--color-text-1); - --ant-menu-item-active-bg: var(--color-white-1); + --ant-menu-item-active-bg: rgba(0, 0, 0, 4%); } .css-var-r0.ant-input-css-var { @@ -263,6 +264,14 @@ body { background-color: var(--color-fill-1); height: 100%; + .ant-pro-sider-actions-list-item { + padding: 0; + + &:hover { + background-color: unset; + } + } + .ant-pro-sider-logo { padding-left: 20px; } @@ -374,6 +383,37 @@ body { } // ======== menu style end ============ +.user-menu-container { + .user-avatar.ant-menu-submenu { + .ant-menu-submenu-title { + padding-left: 8px; + } + } + + &.user-menu-collapsed { + .user-avatar.ant-menu-submenu { + .ant-menu-submenu-title { + padding: 0 !important; + display: flex; + justify-content: center; + + .ant-menu-title-content { + display: none; + } + } + } + } + + .ant-menu-submenu-title { + display: flex; + justify-content: flex-start; + align-items: center; + } +} + +.ant-menu-submenu-popup > .ant-menu { + border-radius: var(--menu-border-radius-base); +} // ======== monaco editor style ============ .monaco-editor { border-radius: 16px; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 76f43a30..4b8c7620 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -216,30 +216,19 @@ export default (props: any) => { fixSiderbar fixedHeader {...runtimeConfig} - rightContentRender={ - runtimeConfig.rightContentRender !== false && - ((layoutProps) => { - const dom = getRightRenderContent({ - runtimeConfig, - loading, - initialState, - setInitialState, - intl - }); - if (runtimeConfig.rightContentRender) { - return runtimeConfig.rightContentRender(layoutProps, dom, { - // BREAK CHANGE userConfig > runtimeConfig - userConfig, - runtimeConfig, - loading, - initialState, - setInitialState, - intl - }); - } - return dom; - }) - } + actionsRender={(layoutProps) => { + const dom = getRightRenderContent({ + runtimeConfig, + loading, + initialState, + setInitialState, + intl, + siderWidth: layoutProps.siderWidth, + collapsed: layoutProps.collapsed + }); + + return dom; + }} > , - label: 'Navigation Two', - children: [ - { key: '5', label: 'Option 5' }, - { key: '6', label: 'Option 6' }, - { - key: 'sub3', - label: 'Submenu' - } - ] - }, - { - key: 'sub4', - label: 'Navigation Three', - icon: , - children: [ - { key: '9', label: 'Option 9' }, - { key: '10', label: 'Option 10' }, - { key: '11', label: 'Option 11' }, - { key: '12', label: 'Option 12' } - ] } ]; diff --git a/src/layouts/rightRender.tsx b/src/layouts/rightRender.tsx index 4f285e9d..c9bed6ad 100644 --- a/src/layouts/rightRender.tsx +++ b/src/layouts/rightRender.tsx @@ -1,22 +1,27 @@ // @ts-nocheck import avatarImg from '@/assets/images/avatar.png'; +import langConfigMap from '@/locales/lang-config-map'; import { GlobalOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons'; -import { SelectLang, history } from '@umijs/max'; -import { Avatar, Dropdown, Menu, Spin, version } from 'antd'; +import { getAllLocales, history, setLocale } from '@umijs/max'; +import { Avatar, Menu, Spin } from 'antd'; +import _ from 'lodash'; export function getRightRenderContent(opts: { runtimeConfig: any; loading: boolean; initialState: any; + collapsed: boolean; setInitialState: any; + siderWidth: number; intl: any; }) { - const { intl } = opts; + const { intl, collapsed, siderWidth } = opts; + const allLocals = getAllLocales(); if (opts.runtimeConfig.rightRender) { return opts.runtimeConfig.rightRender( opts.initialState, @@ -57,118 +62,125 @@ export function getRightRenderContent(opts: { ); } - const renderExtraActions = () => { - return ( - - - - {intl?.formatMessage?.({ id: 'common.settings.language' })} - - - } - > - ); - }; - - // 如果没有打开Locale,并且头像为空就取消掉这个返回的内容 - if (!avatar) return null; - - const renderMenu = () => { - return ( - - -
    - } - title={intl?.formatMessage?.({ id: 'common.button.settings' })} - > - } - title={intl?.formatMessage?.({ id: 'common.button.logout' })} - > -
-
-
- ); - }; const langMenu = { - className: 'umi-plugin-layout-menu', selectedKeys: [], + className: collapsed + ? 'user-menu-container user-menu-collapsed' + : 'user-menu-container', + mode: 'vertical', + expandIcon: false, + inlineCollapsed: collapsed, + triggerSubMenuAction: 'hover', items: [ { - key: 'settings', + key: 'lang', + icon: , label: ( - <> - - {intl?.formatMessage?.({ id: 'common.button.settings' })} - + + {intl?.formatMessage?.({ id: 'common.settings.language' })} + ), - onClick: () => { - history.push('/profile'); - } - }, - { - key: 'logout', - label: ( - <> - - {intl?.formatMessage?.({ id: 'common.button.logout' })} - - ), - onClick: () => { - opts?.runtimeConfig?.logout?.(opts.initialState.currentUser); - } + children: allLocals.map((key) => ({ + key, + label: ( + + {_.get(langConfigMap, [key, 'icon'])} + + {_.get(langConfigMap, [key, 'label'])} + + + ), + onClick: () => { + setLocale(key, false); + } + })) } ] }; - // antd@5 和 4.24 之后推荐使用 menu,性能更好 - let dropdownProps; - if (version.startsWith('5.') || version.startsWith('4.24.')) { - dropdownProps = { menu: langMenu, mode: 'vertical' }; - } else if (version.startsWith('3.')) { - dropdownProps = { - overlay: ( - - {langMenu.items.map((item) => ( - - {item.label} - - ))} - - ) - }; - } else { - // 需要 antd 4.20.0 以上版本 - dropdownProps = { - overlay: - }; - } + const userMenu = { + selectedKeys: [], + className: collapsed + ? 'user-menu-container user-menu-collapsed' + : 'user-menu-container', + mode: 'vertical', + expandIcon: false, + inlineCollapsed: collapsed, + triggerSubMenuAction: 'hover', + items: [ + { + label: '', + key: 'user', + className: 'user-avatar', + icon: ( + + + {_.toUpper(opts.initialState?.currentUser?.username?.charAt(0))} + + {!collapsed && ( + + {opts.initialState?.currentUser?.username} + + )} + + ), + children: [ + { + key: 'settings', + label: ( + + + + {intl?.formatMessage?.({ id: 'common.button.settings' })} + + + ), + onClick: () => { + history.push('/profile'); + } + }, + { + key: 'logout', + label: ( + + + + {intl?.formatMessage?.({ id: 'common.button.logout' })} + + + ), + onClick: () => { + opts?.runtimeConfig?.logout?.(opts.initialState.currentUser); + } + } + ] + } + ] + }; return ( -
- {renderExtraActions()} -
- {opts.runtimeConfig.logout ? ( - <> - - {avatar} - - - ) : ( - avatar - )} -
+
+ +
); } diff --git a/src/locales/lang-config-map.ts b/src/locales/lang-config-map.ts new file mode 100644 index 00000000..6328e77f --- /dev/null +++ b/src/locales/lang-config-map.ts @@ -0,0 +1,326 @@ +export default { + 'ar-EG': { + lang: 'ar-EG', + label: 'العربية', + icon: '🇪🇬', + title: 'لغة' + }, + 'az-AZ': { + lang: 'az-AZ', + label: 'Azərbaycan dili', + icon: '🇦🇿', + title: 'Dil' + }, + 'bg-BG': { + lang: 'bg-BG', + label: 'Български език', + icon: '🇧🇬', + title: 'език' + }, + 'bn-BD': { + lang: 'bn-BD', + label: 'বাংলা', + icon: '🇧🇩', + title: 'ভাষা' + }, + 'ca-ES': { + lang: 'ca-ES', + label: 'Catalá', + icon: '🇨🇦', + title: 'llengua' + }, + 'cs-CZ': { + lang: 'cs-CZ', + label: 'Čeština', + icon: '🇨🇿', + title: 'Jazyk' + }, + 'da-DK': { + lang: 'da-DK', + label: 'Dansk', + icon: '🇩🇰', + title: 'Sprog' + }, + 'de-DE': { + lang: 'de-DE', + label: 'Deutsch', + icon: '🇩🇪', + title: 'Sprache' + }, + 'el-GR': { + lang: 'el-GR', + label: 'Ελληνικά', + icon: '🇬🇷', + title: 'Γλώσσα' + }, + 'en-GB': { + lang: 'en-GB', + label: 'English', + icon: '🇬🇧', + title: 'Language' + }, + 'en-US': { + lang: 'en-US', + label: 'English', + icon: '🇺🇸', + title: 'Language' + }, + 'es-ES': { + lang: 'es-ES', + label: 'Español', + icon: '🇪🇸', + title: 'Idioma' + }, + 'et-EE': { + lang: 'et-EE', + label: 'Eesti', + icon: '🇪🇪', + title: 'Keel' + }, + 'fa-IR': { + lang: 'fa-IR', + label: 'فارسی', + icon: '🇮🇷', + title: 'زبان' + }, + 'fi-FI': { + lang: 'fi-FI', + label: 'Suomi', + icon: '🇫🇮', + title: 'Kieli' + }, + 'fr-BE': { + lang: 'fr-BE', + label: 'Français', + icon: '🇧🇪', + title: 'Langue' + }, + 'fr-FR': { + lang: 'fr-FR', + label: 'Français', + icon: '🇫🇷', + title: 'Langue' + }, + 'ga-IE': { + lang: 'ga-IE', + label: 'Gaeilge', + icon: '🇮🇪', + title: 'Teanga' + }, + 'he-IL': { + lang: 'he-IL', + label: 'עברית', + icon: '🇮🇱', + title: 'שפה' + }, + 'hi-IN': { + lang: 'hi-IN', + label: 'हिन्दी, हिंदी', + icon: '🇮🇳', + title: 'भाषा: हिन्दी' + }, + 'hr-HR': { + lang: 'hr-HR', + label: 'Hrvatski jezik', + icon: '🇭🇷', + title: 'Jezik' + }, + 'hu-HU': { + lang: 'hu-HU', + label: 'Magyar', + icon: '🇭🇺', + title: 'Nyelv' + }, + 'hy-AM': { + lang: 'hu-HU', + label: 'Հայերեն', + icon: '🇦🇲', + title: 'Լեզու' + }, + 'id-ID': { + lang: 'id-ID', + label: 'Bahasa Indonesia', + icon: '🇮🇩', + title: 'Bahasa' + }, + 'it-IT': { + lang: 'it-IT', + label: 'Italiano', + icon: '🇮🇹', + title: 'Linguaggio' + }, + 'is-IS': { + lang: 'is-IS', + label: 'Íslenska', + icon: '🇮🇸', + title: 'Tungumál' + }, + 'ja-JP': { + lang: 'ja-JP', + label: '日本語', + icon: '🇯🇵', + title: '言語' + }, + 'ku-IQ': { + lang: 'ku-IQ', + label: 'کوردی', + icon: '🇮🇶', + title: 'Ziman' + }, + 'kn-IN': { + lang: 'kn-IN', + label: 'ಕನ್ನಡ', + icon: '🇮🇳', + title: 'ಭಾಷೆ' + }, + 'ko-KR': { + lang: 'ko-KR', + label: '한국어', + icon: '🇰🇷', + title: '언어' + }, + 'lv-LV': { + lang: 'lv-LV', + label: 'Latviešu valoda', + icon: '🇱🇮', + title: 'Kalba' + }, + 'mk-MK': { + lang: 'mk-MK', + label: 'македонски јазик', + icon: '🇲🇰', + title: 'Јазик' + }, + 'mn-MN': { + lang: 'mn-MN', + label: 'Монгол хэл', + icon: '🇲🇳', + title: 'Хэл' + }, + 'ms-MY': { + lang: 'ms-MY', + label: 'بهاس ملايو‎', + icon: '🇲🇾', + title: 'Bahasa' + }, + 'nb-NO': { + lang: 'nb-NO', + label: 'Norsk', + icon: '🇳🇴', + title: 'Språk' + }, + 'ne-NP': { + lang: 'ne-NP', + label: 'नेपाली', + icon: '🇳🇵', + title: 'भाषा' + }, + 'nl-BE': { + lang: 'nl-BE', + label: 'Vlaams', + icon: '🇧🇪', + title: 'Taal' + }, + 'nl-NL': { + lang: 'nl-NL', + label: 'Nederlands', + icon: '🇳🇱', + title: 'Taal' + }, + 'pl-PL': { + lang: 'pl-PL', + label: 'Polski', + icon: '🇵🇱', + title: 'Język' + }, + 'pt-BR': { + lang: 'pt-BR', + label: 'Português', + icon: '🇧🇷', + title: 'Idiomas' + }, + 'pt-PT': { + lang: 'pt-PT', + label: 'Português', + icon: '🇵🇹', + title: 'Idiomas' + }, + 'ro-RO': { + lang: 'ro-RO', + label: 'Română', + icon: '🇷🇴', + title: 'Limba' + }, + 'ru-RU': { + lang: 'ru-RU', + label: 'Русский', + icon: '🇷🇺', + title: 'язык' + }, + 'sk-SK': { + lang: 'sk-SK', + label: 'Slovenčina', + icon: '🇸🇰', + title: 'Jazyk' + }, + 'sr-RS': { + lang: 'sr-RS', + label: 'српски језик', + icon: '🇸🇷', + title: 'Језик' + }, + 'sl-SI': { + lang: 'sl-SI', + label: 'Slovenščina', + icon: '🇸🇱', + title: 'Jezik' + }, + 'sv-SE': { + lang: 'sv-SE', + label: 'Svenska', + icon: '🇸🇪', + title: 'Språk' + }, + 'ta-IN': { + lang: 'ta-IN', + label: 'தமிழ்', + icon: '🇮🇳', + title: 'மொழி' + }, + 'th-TH': { + lang: 'th-TH', + label: 'ไทย', + icon: '🇹🇭', + title: 'ภาษา' + }, + 'tr-TR': { + lang: 'tr-TR', + label: 'Türkçe', + icon: '🇹🇷', + title: 'Dil' + }, + 'uk-UA': { + lang: 'uk-UA', + label: 'Українська', + icon: '🇺🇰', + title: 'Мова' + }, + 'vi-VN': { + lang: 'vi-VN', + label: 'Tiếng Việt', + icon: '🇻🇳', + title: 'Ngôn ngữ' + }, + 'zh-CN': { + lang: 'zh-CN', + label: '简体中文', + icon: '🇨🇳', + title: '语言' + }, + 'zh-TW': { + lang: 'zh-TW', + label: '繁體中文', + icon: '🇭🇰', + title: '語言' + } +}; diff --git a/src/pages/login/components/styles.less b/src/pages/login/components/styles.less index 25a7951a..339b449f 100644 --- a/src/pages/login/components/styles.less +++ b/src/pages/login/components/styles.less @@ -9,6 +9,10 @@ :global(.field-wrapper) { background-color: transparent !important; } + + :global(.ant-input-outlined.ant-input-status-error:not(.ant-input-disabled)) { + background-color: transparent !important; + } } :local(.box) { diff --git a/src/pages/playground/components/reference-params.tsx b/src/pages/playground/components/reference-params.tsx index 68925035..88cfee30 100644 --- a/src/pages/playground/components/reference-params.tsx +++ b/src/pages/playground/components/reference-params.tsx @@ -7,7 +7,7 @@ interface ReferenceParamsProps { completion_tokens: number; prompt_tokens: number; total_tokens: number; - time_per_output_token_ms: number; + tokens_per_second: number; }; } @@ -43,7 +43,7 @@ const ReferenceParams = (props: ReferenceParamsProps) => { {intl.formatMessage({ id: 'playground.tokenoutput' })}:{' '} - {usage.time_per_output_token_ms * 1000} Tokens/s + {usage.tokens_per_second * 1000} Tokens/s
); diff --git a/src/pages/playground/components/view-code-modal.tsx b/src/pages/playground/components/view-code-modal.tsx index 2f1082f4..c873f2d4 100644 --- a/src/pages/playground/components/view-code-modal.tsx +++ b/src/pages/playground/components/view-code-modal.tsx @@ -53,7 +53,7 @@ const ViewCodeModal: React.FC = (props) => { const systemList = systemMessage ? [{ role: 'system', content: systemMessage }] : []; - const code = `curl ${window.location.origin}/v1-openai/chat/completions \\ \n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify( + const code = `curl ${window.location.origin}/v1-openai/chat/completions \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify( { ...parameters, messages: [...systemList, ...messageList] diff --git a/src/pages/users/components/add-modal.tsx b/src/pages/users/components/add-modal.tsx index eb37da84..046a9f5e 100644 --- a/src/pages/users/components/add-modal.tsx +++ b/src/pages/users/components/add-modal.tsx @@ -88,6 +88,7 @@ const AddModal: React.FC = ({ name="full_name" rules={[{ required: false }]}>