From 69f354b2781030432a02e4cf8c7cae9e5d88c998 Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 30 May 2024 09:26:09 +0800 Subject: [PATCH] feat: user settings --- config/routes.ts | 10 ++- package.json | 1 + pnpm-lock.yaml | 33 ++++++++ src/components/editor-wrap/index.less | 28 +++++++ src/components/editor-wrap/index.tsx | 54 ++++++++++++ src/components/form-buttons/index.tsx | 27 ++++++ src/global.less | 6 ++ src/layouts/rightRender.tsx | 60 +++++++++---- .../components/add-modal.tsx | 0 src/pages/{llm-models => llmodels}/index.tsx | 0 .../playground/components/ground-left.tsx | 13 ++- .../playground/components/params-settings.tsx | 9 +- .../playground/components/view-code-modal.tsx | 84 +++++++++++++++++++ src/pages/profile/index.tsx | 79 +++++++++++++++++ 14 files changed, 382 insertions(+), 22 deletions(-) create mode 100644 src/components/editor-wrap/index.less create mode 100644 src/components/editor-wrap/index.tsx create mode 100644 src/components/form-buttons/index.tsx rename src/pages/{llm-models => llmodels}/components/add-modal.tsx (100%) rename src/pages/{llm-models => llmodels}/index.tsx (100%) create mode 100644 src/pages/playground/components/view-code-modal.tsx create mode 100644 src/pages/profile/index.tsx diff --git a/config/routes.ts b/config/routes.ts index 3d31c389..05c158fe 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -26,7 +26,7 @@ export default [ path: '/models', key: 'models', icon: 'Block', - component: './llm-models' + component: './llmodels' }, { name: 'Nodes', @@ -49,6 +49,14 @@ export default [ icon: 'Team', component: './users' }, + { + name: 'Profile', + path: '/profile', + key: 'profile', + hideInMenu: true, + component: './profile', + icon: 'User' + }, { name: '404', path: '*', diff --git a/package.json b/package.json index 1ffe83ba..98ebe090 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@ant-design/icons": "^5.3.7", "@ant-design/plots": "^2.2.2", "@ant-design/pro-components": "^2.7.1", + "@monaco-editor/react": "^4.6.0", "@types/lodash": "^4.17.4", "@umijs/max": "^4.2.1", "antd": "^5.17.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31fb8670..fb86cb72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@ant-design/pro-components': specifier: ^2.7.1 version: 2.7.1(antd@5.17.0)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1) + '@monaco-editor/react': + specifier: ^4.6.0 + version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1)(react@18.3.1) '@types/lodash': specifier: ^4.17.4 version: 4.17.4 @@ -2418,6 +2421,28 @@ packages: react-is: 16.13.1 dev: false + /@monaco-editor/loader@1.4.0(monaco-editor@0.49.0): + resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==} + peerDependencies: + monaco-editor: '>= 0.21.0 < 1' + dependencies: + monaco-editor: 0.49.0 + state-local: 1.0.7 + dev: false + + /@monaco-editor/react@4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@monaco-editor/loader': 1.4.0(monaco-editor@0.49.0) + monaco-editor: 0.49.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} dependencies: @@ -8316,6 +8341,10 @@ packages: resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} dev: false + /monaco-editor@0.49.0: + resolution: {integrity: sha512-2I8/T3X/hLxB2oPHgqcNYUVdA/ZEFShT7IAujifIPMfKkNbLOqY8XCoyHCXrsdjb36dW9MwoTwBCFpXKMwNwaQ==} + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false @@ -11552,6 +11581,10 @@ packages: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} dev: false + /state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + dev: false + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} diff --git a/src/components/editor-wrap/index.less b/src/components/editor-wrap/index.less new file mode 100644 index 00000000..df052fa5 --- /dev/null +++ b/src/components/editor-wrap/index.less @@ -0,0 +1,28 @@ +.editor-wrap { + border-radius: 16px; + overflow: hidden; + .editor-header { + display: flex; + padding: 6px 10px; + justify-content: space-between; + align-items: center; + background-color: rgb(56, 56, 56); + .ant-select-selector { + background-color: rgba(255, 255, 255, 0.09) !important; + color: rgba(255, 255, 255, 0.7); + .ant-select-selection-item { + color: rgba(255, 255, 255, 0.7); + } + } + .ant-select-arrow { + color: rgba(255, 255, 255, 0.7); + } + } + // set scrollbar style + + .scrollbar { + .slider { + border-radius: 6px; + } + } +} diff --git a/src/components/editor-wrap/index.tsx b/src/components/editor-wrap/index.tsx new file mode 100644 index 00000000..cb4b5920 --- /dev/null +++ b/src/components/editor-wrap/index.tsx @@ -0,0 +1,54 @@ +import { Select } from 'antd'; +import React from 'react'; +import CopyButton from '../copy-button'; +import './index.less'; + +interface EditorwrapProps { + header?: React.ReactNode; + children: React.ReactNode; + showHeader?: boolean; + copyText: string; + langOptions?: { label: string; value: string }[]; + onChangeLang?: (value: string) => void; +} +const EditorWrap: React.FC = ({ + header, + children, + copyText, + langOptions, + onChangeLang, + showHeader = true +}) => { + const handleChangeLang = (value: string) => { + onChangeLang && onChangeLang(value); + }; + const renderHeader = () => { + if (header) { + return
{header}
; + } + if (showHeader) { + return ( +
+ + +
+ ); + } + return null; + }; + return ( +
+ {renderHeader()} +
{children}
+
+ ); +}; + +export default EditorWrap; diff --git a/src/components/form-buttons/index.tsx b/src/components/form-buttons/index.tsx new file mode 100644 index 00000000..dc5a2548 --- /dev/null +++ b/src/components/form-buttons/index.tsx @@ -0,0 +1,27 @@ +import { Button, Space } from 'antd'; + +type FormButtonsProps = { + onOk: () => void; + onCancel: () => void; + cancelText?: string; + okText?: string; +}; +const FormButtons: React.FC = ({ + onOk, + onCancel, + cancelText, + okText +}) => { + return ( + + + + + ); +}; + +export default FormButtons; diff --git a/src/global.less b/src/global.less index 7458c07b..e90f323f 100644 --- a/src/global.less +++ b/src/global.less @@ -230,5 +230,11 @@ body { } } // ======== menu style end ============ + +// ======== monaco editor style ============ +.monaco-editor { + border-radius: 16px; +} + @import url('src/assets/styles/common.less'); @import url('src/assets/styles/menu.less'); diff --git a/src/layouts/rightRender.tsx b/src/layouts/rightRender.tsx index 00fadd7d..036d52c5 100644 --- a/src/layouts/rightRender.tsx +++ b/src/layouts/rightRender.tsx @@ -1,7 +1,13 @@ // @ts-nocheck import avatarImg from '@/assets/images/avatar.png'; -import { LogoutOutlined } from '@ant-design/icons'; +import { + GlobalOutlined, + LogoutOutlined, + SettingOutlined, + SunOutlined +} from '@ant-design/icons'; +import { useNavigate } from '@umijs/max'; import { Avatar, Dropdown, Menu, Spin, version } from 'antd'; export function getRightRenderContent(opts: { @@ -52,10 +58,48 @@ export function getRightRenderContent(opts: { // 如果没有打开Locale,并且头像为空就取消掉这个返回的内容 if (!avatar) return null; + const navigate = useNavigate(); + const langMenu = { className: 'umi-plugin-layout-menu', selectedKeys: [], items: [ + { + key: 'settings', + label: ( + <> + + 设置 + + ), + onClick: () => { + navigate('/profile'); + } + }, + { + key: 'theme', + label: ( + <> + + 外观 + + ), + onClick: () => { + console.log('theme'); + } + }, + { + key: 'lang', + label: ( + <> + + 语言 + + ), + onClick: () => { + console.log('lang'); + } + }, { key: 'logout', label: ( @@ -67,20 +111,6 @@ export function getRightRenderContent(opts: { onClick: () => { opts?.runtimeConfig?.logout?.(opts.initialState); } - }, - { - key: 'theme', - label: <>外观, - onClick: () => { - console.log('theme'); - } - }, - { - key: 'lang', - label: '语言', - onClick: () => { - console.log('lang'); - } } ] }; diff --git a/src/pages/llm-models/components/add-modal.tsx b/src/pages/llmodels/components/add-modal.tsx similarity index 100% rename from src/pages/llm-models/components/add-modal.tsx rename to src/pages/llmodels/components/add-modal.tsx diff --git a/src/pages/llm-models/index.tsx b/src/pages/llmodels/index.tsx similarity index 100% rename from src/pages/llm-models/index.tsx rename to src/pages/llmodels/index.tsx diff --git a/src/pages/playground/components/ground-left.tsx b/src/pages/playground/components/ground-left.tsx index 596ee06b..be5596c4 100644 --- a/src/pages/playground/components/ground-left.tsx +++ b/src/pages/playground/components/ground-left.tsx @@ -4,6 +4,7 @@ import '../style/ground-left.less'; import ChatFooter from './chat-footer'; import MessageItem from './message-item'; import ReferenceParams from './reference-params'; +import ViewCodeModal from './view-code-modal'; const MessageList: React.FC = () => { const [messageList, setMessageList] = useState([ @@ -16,6 +17,7 @@ const MessageList: React.FC = () => { message: 'hello, nice to meet you!' } ]); + const [show, setShow] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); const handleNewMessage = () => { @@ -33,13 +35,17 @@ const MessageList: React.FC = () => { }; const handleView = () => { - console.log('view'); + setShow(true); }; const handleSubmit = () => { console.log('submit'); }; + const handleCloseViewCode = () => { + setShow(false); + }; + const handleDelete = (index: number) => { messageList.splice(index, 1); setMessageList([...messageList]); @@ -71,6 +77,11 @@ const MessageList: React.FC = () => { feedback={} > + ); }; diff --git a/src/pages/playground/components/params-settings.tsx b/src/pages/playground/components/params-settings.tsx index 60514952..b6659350 100644 --- a/src/pages/playground/components/params-settings.tsx +++ b/src/pages/playground/components/params-settings.tsx @@ -68,10 +68,9 @@ const ParamsSettings: React.FC<{ onClose: () => void }> = ({ onClose }) => { name="temperature" rules={[{ required: true }]} > - + + + name="maxTokens" @@ -86,7 +85,7 @@ const ParamsSettings: React.FC<{ onClose: () => void }> = ({ onClose }) => { name="topP" rules={[{ required: true }]} > - + diff --git a/src/pages/playground/components/view-code-modal.tsx b/src/pages/playground/components/view-code-modal.tsx new file mode 100644 index 00000000..a478275e --- /dev/null +++ b/src/pages/playground/components/view-code-modal.tsx @@ -0,0 +1,84 @@ +import EditorWrap from '@/components/editor-wrap'; +import Editor from '@monaco-editor/react'; +import { Modal } from 'antd'; +import React, { useRef, useState } from 'react'; + +type ViewModalProps = { + title: string; + open: boolean; + onCancel: () => void; +}; + +const ViewCodeModal: React.FC = (props) => { + const { title, open, onCancel } = props || {}; + if (!open) { + return null; + } + const editorRef = useRef(null); + const [codeValue, setCodeValue] = useState(''); + const [lang, setLang] = useState('json'); + + const langOptions = [ + { label: 'curl', value: 'curl' }, + { label: 'JSON', value: 'json' }, + { label: 'JavaScript', value: 'javascript' }, + { label: 'Python', value: 'python' } + ]; + + const handleEditorDidMount = (editor: any, monaco: any) => { + editorRef.current = editor; + }; + + const handleOnChangeLang = (value: string) => { + setLang(value); + }; + const editorConfig = { + minimap: { + enabled: false + }, + scrollbar: { + verticalSliderSize: 8 + } + }; + return ( + +
+ You can use the following code to start integrating your current prompt + and settings into your application. +
+ + + +
+ our API Key can be foundhere You should use environment variables or a + secret management tool to expose your key to your applications. +
+
+ ); +}; + +export default ViewCodeModal; diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx new file mode 100644 index 00000000..46239a30 --- /dev/null +++ b/src/pages/profile/index.tsx @@ -0,0 +1,79 @@ +import FormButtons from '@/components/form-buttons'; +import SealInput from '@/components/seal-form/seal-input'; +import { INPUT_WIDTH } from '@/constants'; +import { PageContainer } from '@ant-design/pro-components'; +import { Form } from 'antd'; +import { StrictMode } from 'react'; + +interface ProfileProps { + name: string; + password: string; + originalPassword: string; + email: string; +} +const Profile: React.FC = () => { + const [form] = Form.useForm(); + + const handleOnFinish = (values: any) => { + console.log('handleOnFinish', values); + }; + + const handleOnFinishFailed = (errorInfo: any) => { + console.log('handleOnFinishFailed', errorInfo); + }; + + return ( + + +
+ name="name" rules={[{ required: true }]}> + + + name="email" rules={[{ required: true }]}> + + + + name="originalPassword" + rules={[{ required: true }]} + > + + + name="password" rules={[{ required: true }]}> + + + + +
+
+ ); +}; + +export default Profile;