From c5b41900df79b4e85811a0e1656b455a10e5adf3 Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 3 Jun 2024 19:09:43 +0800 Subject: [PATCH] chore: resource list --- config/routes.ts | 8 +- .../seal-form/components/wrapper.less | 8 +- .../seal-form/components/wrapper.tsx | 3 + src/components/seal-form/input-number.tsx | 15 +- src/components/seal-form/input-search.tsx | 15 +- src/components/seal-form/password.tsx | 15 +- src/components/seal-form/seal-input.tsx | 32 ++- src/components/seal-form/seal-select.tsx | 17 +- src/components/seal-form/types.ts | 4 +- src/components/transition/index.less | 28 ++ src/components/transition/index.tsx | 56 ++++ src/global.less | 2 +- src/pages/Resources/components/gpus.tsx | 254 ++++++++++++++++ src/pages/Resources/components/nodes.tsx | 254 ++++++++++++++++ .../components/render-progress.tsx | 0 .../{nodes => Resources}/config/types.ts | 0 src/pages/Resources/index.tsx | 46 +++ .../dashboard/components/model-nodes.tsx | 2 +- src/pages/dashboard/index.tsx | 28 +- src/pages/llmodels/index.tsx | 13 + src/pages/nodes/index.tsx | 271 ------------------ .../playground/components/ground-left.tsx | 32 ++- .../playground/components/message-item.tsx | 2 +- src/pages/playground/style/message-item.less | 20 ++ .../playground/style/system-message-wrap.less | 5 + 25 files changed, 827 insertions(+), 303 deletions(-) create mode 100644 src/components/transition/index.less create mode 100644 src/components/transition/index.tsx create mode 100644 src/pages/Resources/components/gpus.tsx create mode 100644 src/pages/Resources/components/nodes.tsx rename src/pages/{nodes => Resources}/components/render-progress.tsx (100%) rename src/pages/{nodes => Resources}/config/types.ts (100%) create mode 100644 src/pages/Resources/index.tsx delete mode 100644 src/pages/nodes/index.tsx create mode 100644 src/pages/playground/style/system-message-wrap.less diff --git a/config/routes.ts b/config/routes.ts index 85d62722..d816a057 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -29,11 +29,11 @@ export default [ component: './llmodels' }, { - name: 'Nodes', - path: '/nodes', - key: 'nodes', + name: 'Resources', + path: '/resources', + key: 'resources', icon: 'CloudServer', - component: './nodes' + component: './resources' }, { name: 'API Keys', diff --git a/src/components/seal-form/components/wrapper.less b/src/components/seal-form/components/wrapper.less index c56e5380..f68ce422 100644 --- a/src/components/seal-form/components/wrapper.less +++ b/src/components/seal-form/components/wrapper.less @@ -1,7 +1,7 @@ :local(.wrapper) { @wrapheight: 54px; @inputheight: 32px; - @borderRadius: 24px; + @borderRadius: 20px; position: relative; display: flex; flex-direction: column; @@ -13,6 +13,12 @@ border-radius: @borderRadius; padding-inline: 12px; padding-block: 20px 0; + .extra { + position: absolute; + right: 12px; + top: 8px; + z-index: 10; + } &.seal-input-wrapper-disabled { background-color: var(--ant-color-bg-container-disabled); diff --git a/src/components/seal-form/components/wrapper.tsx b/src/components/seal-form/components/wrapper.tsx index b38228ac..5da9eb28 100644 --- a/src/components/seal-form/components/wrapper.tsx +++ b/src/components/seal-form/components/wrapper.tsx @@ -11,6 +11,7 @@ interface WrapperProps { description?: React.ReactNode; className?: string; disabled?: boolean; + extra?: React.ReactNode; onClick?: () => void; } @@ -23,6 +24,7 @@ const Wrapper: React.FC = ({ disabled, description, required, + extra, onClick }) => { return ( @@ -50,6 +52,7 @@ const Wrapper: React.FC = ({ description={description} > + {extra &&
{extra}
} {children} ); diff --git a/src/components/seal-form/input-number.tsx b/src/components/seal-form/input-number.tsx index bf057363..d52f0e4a 100644 --- a/src/components/seal-form/input-number.tsx +++ b/src/components/seal-form/input-number.tsx @@ -7,10 +7,21 @@ import { SealFormItemProps } from './types'; const SealInputNumber: React.FC = ( props ) => { - const { label, placeholder, required, description, ...rest } = props; + const { + label, + placeholder, + required, + description, + isInFormItems = true, + ...rest + } = props; const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const { status } = Form.Item.useStatus(); + let status = ''; + if (isInFormItems) { + const statusData = Form?.Item?.useStatus?.(); + status = statusData?.status || ''; + } useEffect(() => { if (props.value) { diff --git a/src/components/seal-form/input-search.tsx b/src/components/seal-form/input-search.tsx index 445ebcd6..206b0b54 100644 --- a/src/components/seal-form/input-search.tsx +++ b/src/components/seal-form/input-search.tsx @@ -16,10 +16,21 @@ type OnSearch = ( ) => void; const SealInputSearch: React.FC = (props) => { - const { label, placeholder, required, description, ...rest } = props; + const { + label, + placeholder, + required, + description, + isInFormItems = true, + ...rest + } = props; const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const { status } = Form.Item.useStatus(); + let status = ''; + if (isInFormItems) { + const statusData = Form?.Item?.useStatus?.(); + status = statusData?.status || ''; + } useEffect(() => { if (props.value) { diff --git a/src/components/seal-form/password.tsx b/src/components/seal-form/password.tsx index 5fa27e08..cfa5dc1d 100644 --- a/src/components/seal-form/password.tsx +++ b/src/components/seal-form/password.tsx @@ -5,10 +5,21 @@ import Wrapper from './components/wrapper'; import { SealFormItemProps } from './types'; const SealPassword: React.FC = (props) => { - const { label, placeholder, required, description, ...rest } = props; + const { + label, + placeholder, + required, + description, + isInFormItems = true, + ...rest + } = props; const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const { status } = Form.Item.useStatus(); + let status = ''; + if (isInFormItems) { + const statusData = Form?.Item?.useStatus?.(); + status = statusData?.status || ''; + } useEffect(() => { if (props.value) { diff --git a/src/components/seal-form/seal-input.tsx b/src/components/seal-form/seal-input.tsx index e9d4285b..9fb9663b 100644 --- a/src/components/seal-form/seal-input.tsx +++ b/src/components/seal-form/seal-input.tsx @@ -18,12 +18,19 @@ const SealTextArea: React.FC = (props) => { onInput, style, required, + isInFormItems = true, description, + variant, + extra, ...rest } = props; const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const { status } = Form.Item.useStatus(); + let status = ''; + if (isInFormItems) { + const statusData = Form?.Item?.useStatus?.(); + status = statusData?.status || ''; + } useEffect(() => { if (props.value) { @@ -57,7 +64,11 @@ const SealTextArea: React.FC = (props) => { const handleOnBlur = useCallback( (e: any) => { - if (!inputRef.current?.input?.value) { + console.log( + 'inputRef.current==========', + inputRef.current?.resizableTextArea?.textArea?.value + ); + if (!inputRef.current?.resizableTextArea?.textArea?.value) { setIsFocus(false); onBlur?.(e); } @@ -80,6 +91,8 @@ const SealTextArea: React.FC = (props) => { required={required} description={description} className="seal-textarea-wrapper" + variant={variant} + extra={extra} disabled={props.disabled} onClick={handleClickWrapper} > @@ -98,10 +111,21 @@ const SealTextArea: React.FC = (props) => { }; const SealInput: React.FC = (props) => { - const { label, placeholder, required, description, ...rest } = props; + const { + label, + placeholder, + required, + description, + isInFormItems = true, + ...rest + } = props; const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const { status } = Form.Item.useStatus(); + let status = ''; + if (isInFormItems) { + const statusData = Form?.Item?.useStatus?.(); + status = statusData?.status || ''; + } useEffect(() => { if (props.value) { diff --git a/src/components/seal-form/seal-select.tsx b/src/components/seal-form/seal-select.tsx index e927dd7d..8ab74618 100644 --- a/src/components/seal-form/seal-select.tsx +++ b/src/components/seal-form/seal-select.tsx @@ -6,11 +6,22 @@ import Wrapper from './components/wrapper'; import { SealFormItemProps } from './types'; const SealSelect: React.FC = (props) => { - const { label, placeholder, children, required, description, ...rest } = - props; + const { + label, + placeholder, + children, + required, + description, + isInFormItems = true, + ...rest + } = props; const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); - const { status } = Form.Item.useStatus(); + let status = ''; + if (isInFormItems) { + const statusData = Form?.Item?.useStatus?.(); + status = statusData?.status || ''; + } useEffect(() => { if (isNotEmptyValue(props.value)) { diff --git a/src/components/seal-form/types.ts b/src/components/seal-form/types.ts index 85bbffc8..0d134baa 100644 --- a/src/components/seal-form/types.ts +++ b/src/components/seal-form/types.ts @@ -1,7 +1,9 @@ import React from 'react'; export interface SealFormItemProps { - label?: string; + label?: React.ReactNode; required?: boolean; + isInFormItems?: boolean; description?: React.ReactNode; + extra?: React.ReactNode; } diff --git a/src/components/transition/index.less b/src/components/transition/index.less new file mode 100644 index 00000000..1b42fc49 --- /dev/null +++ b/src/components/transition/index.less @@ -0,0 +1,28 @@ +.transition-wrapper { + border-radius: 16px; + display: flex; + flex-direction: column; + overflow: hidden; + &.bordered { + border-width: var(--ant-line-width); + border-style: var(--ant-line-type); + border-color: var(--ant-color-border); + background-color: var(--color-white-1); + } + &.filled { + border-color: var(--ant-color-border); + .content-wrapper { + background-color: var(--color-fill-1); + } + } + .header { + background-color: var(--color-fill-1); + cursor: pointer; + padding: 8px 16px; + } + + .content-wrapper { + overflow: hidden; + transition: height 300ms ease-in-out; + } +} diff --git a/src/components/transition/index.tsx b/src/components/transition/index.tsx new file mode 100644 index 00000000..11b2c590 --- /dev/null +++ b/src/components/transition/index.tsx @@ -0,0 +1,56 @@ +import classNames from 'classnames'; +import { useEffect, useRef, useState } from 'react'; +import './index.less'; + +interface TransitionWrapProps { + minHeight?: number; + header?: React.ReactNode; + variant?: 'bordered' | 'filled'; + children: React.ReactNode; +} +const TransitionWrapper: React.FC = (props) => { + const { minHeight = 50, header, variant = 'bordered', children } = props; + const [isOpen, setIsOpen] = useState(true); + const [height, setHeight] = useState(0); + const contentRef = useRef(null); + + useEffect(() => { + if (isOpen) { + setHeight(contentRef?.current?.scrollHeight); + } else { + setHeight(0); + } + }, [isOpen]); + + const toggleOpen = () => { + setIsOpen(!isOpen); + }; + + return ( +
+
+ {header} +
+
+
{children}
+
+
+ ); +}; + +export default TransitionWrapper; diff --git a/src/global.less b/src/global.less index e90f323f..01dfc085 100644 --- a/src/global.less +++ b/src/global.less @@ -47,7 +47,7 @@ html { --ant-font-size-xl: 20px; --ant-font-size: var(--font-size-base); --ant-padding-sm: 14px; - --ant-border-radius-lg: 20px; + --ant-border-radius-lg: 16px; --ant-color-error: #ff4d4f; --ant-color-bg-mask: rgba(0, 0, 0, 0.35); diff --git a/src/pages/Resources/components/gpus.tsx b/src/pages/Resources/components/gpus.tsx new file mode 100644 index 00000000..417a0dda --- /dev/null +++ b/src/pages/Resources/components/gpus.tsx @@ -0,0 +1,254 @@ +import PageTools from '@/components/page-tools'; +import StatusTag from '@/components/status-tag'; +import useTableRowSelection from '@/hooks/use-table-row-selection'; +import useTableSort from '@/hooks/use-table-sort'; +import { SyncOutlined } from '@ant-design/icons'; +import { Button, Input, Space, Table } from 'antd'; +import { useState } from 'react'; +import { NodeItem } from '../config/types'; +import RenderProgress from './render-progress'; +const { Column } = Table; + +const dataSource: NodeItem[] = [ + { + id: 1, + name: 'bj-web-service-1', + address: '183.14.31.136', + hostname: 'bj-web-service-1', + labels: {}, + resources: { + capacity: { + cpu: 4, + gpu: 2, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 2.5, + gpu: 1.6, + memory: '64', + gram: '24 Gib' + } + }, + state: 'ACTIVE' + }, + { + id: 2, + name: 'bj-db-service-2', + address: '172.24.1.36', + hostname: 'bj-db-service-2', + labels: {}, + resources: { + capacity: { + cpu: 4, + gpu: 2, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 2, + gpu: 1.5, + memory: '32 GiB', + gram: '12 Gib' + } + }, + state: 'ACTIVE' + }, + { + id: 3, + name: 'guangzhou-computed-node-2', + address: '170.10.2.10', + hostname: 'guangzhou-computed-node-2', + labels: {}, + resources: { + capacity: { + cpu: 8, + gpu: 4, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 2, + gpu: 1.5, + memory: '32 GiB', + gram: '12 Gib' + } + }, + state: 'ACTIVE' + }, + { + id: 4, + name: 'hangzhou-cache-node-1', + address: '115.2.21.10', + hostname: 'hangzhou-cache-node-1', + labels: {}, + resources: { + capacity: { + cpu: 8, + gpu: 4, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 4, + gpu: 2.5, + memory: '40 GiB', + gram: '16 Gib' + } + }, + state: 'ACTIVE' + } +]; + +const Models: React.FC = () => { + const rowSelection = useTableRowSelection(); + const { sortOrder, setSortOrder } = useTableSort({ + defaultSortOrder: 'descend' + }); + const [total, setTotal] = useState(10); + const [loading, setLoading] = useState(false); + const [queryParams, setQueryParams] = useState({ + current: 1, + pageSize: 10, + name: '' + }); + const handleShowSizeChange = (current: number, size: number) => { + console.log(current, size); + }; + + const handlePageChange = (page: number, pageSize: number | undefined) => { + console.log(page, pageSize); + }; + + const handleTableChange = (pagination: any, filters: any, sorter: any) => { + setSortOrder(sorter.order); + }; + + const fetchData = async () => { + console.log('fetchData'); + }; + const handleSearch = (e: any) => { + fetchData(); + }; + + const handleNameChange = (e: any) => { + setQueryParams({ + ...queryParams, + name: e.target.value + }); + }; + + return ( + <> + + + + + } + > + + + { + return ( + + ); + }} + /> + + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + + + ); + }} + /> +
+ + ); +}; + +export default Models; diff --git a/src/pages/Resources/components/nodes.tsx b/src/pages/Resources/components/nodes.tsx new file mode 100644 index 00000000..edf85840 --- /dev/null +++ b/src/pages/Resources/components/nodes.tsx @@ -0,0 +1,254 @@ +import PageTools from '@/components/page-tools'; +import StatusTag from '@/components/status-tag'; +import useTableRowSelection from '@/hooks/use-table-row-selection'; +import useTableSort from '@/hooks/use-table-sort'; +import { SyncOutlined } from '@ant-design/icons'; +import { Button, Input, Space, Table } from 'antd'; +import { useState } from 'react'; +import { NodeItem } from '../config/types'; +import RenderProgress from './render-progress'; +const { Column } = Table; + +const dataSource: NodeItem[] = [ + { + id: 1, + name: 'bj-web-service-1', + address: '183.14.31.136', + hostname: 'bj-web-service-1', + labels: {}, + resources: { + capacity: { + cpu: 4, + gpu: 2, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 2.5, + gpu: 1.6, + memory: '64', + gram: '24 Gib' + } + }, + state: 'ACTIVE' + }, + { + id: 2, + name: 'bj-db-service-2', + address: '172.24.1.36', + hostname: 'bj-db-service-2', + labels: {}, + resources: { + capacity: { + cpu: 4, + gpu: 2, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 2, + gpu: 1.5, + memory: '32 GiB', + gram: '12 Gib' + } + }, + state: 'ACTIVE' + }, + { + id: 3, + name: 'guangzhou-computed-node-2', + address: '170.10.2.10', + hostname: 'guangzhou-computed-node-2', + labels: {}, + resources: { + capacity: { + cpu: 8, + gpu: 4, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 2, + gpu: 1.5, + memory: '32 GiB', + gram: '12 Gib' + } + }, + state: 'ACTIVE' + }, + { + id: 4, + name: 'hangzhou-cache-node-1', + address: '115.2.21.10', + hostname: 'hangzhou-cache-node-1', + labels: {}, + resources: { + capacity: { + cpu: 8, + gpu: 4, + memory: '64 GiB', + gram: '24 Gib' + }, + allocable: { + cpu: 4, + gpu: 2.5, + memory: '40 GiB', + gram: '16 Gib' + } + }, + state: 'ACTIVE' + } +]; + +const Models: React.FC = () => { + const rowSelection = useTableRowSelection(); + const { sortOrder, setSortOrder } = useTableSort({ + defaultSortOrder: 'descend' + }); + const [total, setTotal] = useState(10); + const [loading, setLoading] = useState(false); + const [queryParams, setQueryParams] = useState({ + current: 1, + pageSize: 10, + name: '' + }); + const handleShowSizeChange = (current: number, size: number) => { + console.log(current, size); + }; + + const handlePageChange = (page: number, pageSize: number | undefined) => { + console.log(page, pageSize); + }; + + const handleTableChange = (pagination: any, filters: any, sorter: any) => { + setSortOrder(sorter.order); + }; + + const fetchData = async () => { + console.log('fetchData'); + }; + const handleSearch = (e: any) => { + fetchData(); + }; + + const handleNameChange = (e: any) => { + setQueryParams({ + ...queryParams, + name: e.target.value + }); + }; + + return ( + <> + + + + + } + > + + + { + return ( + + ); + }} + /> + + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + + + ); + }} + /> +
+ + ); +}; + +export default Models; diff --git a/src/pages/nodes/components/render-progress.tsx b/src/pages/Resources/components/render-progress.tsx similarity index 100% rename from src/pages/nodes/components/render-progress.tsx rename to src/pages/Resources/components/render-progress.tsx diff --git a/src/pages/nodes/config/types.ts b/src/pages/Resources/config/types.ts similarity index 100% rename from src/pages/nodes/config/types.ts rename to src/pages/Resources/config/types.ts diff --git a/src/pages/Resources/index.tsx b/src/pages/Resources/index.tsx new file mode 100644 index 00000000..f00e4261 --- /dev/null +++ b/src/pages/Resources/index.tsx @@ -0,0 +1,46 @@ +import { PageContainer } from '@ant-design/pro-components'; +import type { TabsProps } from 'antd'; +import { Tabs } from 'antd'; +import { useState } from 'react'; +import GPUs from './components/gpus'; +import Nodes from './components/nodes'; + +const items: TabsProps['items'] = [ + { + key: 'nodes', + label: 'Nodes', + children: + }, + { + key: 'gpus', + label: 'GPUs', + children: + } +]; +const Resources = () => { + const [activeKey, setActiveKey] = useState('nodes'); + + const handleChangeTab = (key: string) => { + setActiveKey(key); + }; + + return ( + + + + ); +}; + +export default Resources; diff --git a/src/pages/dashboard/components/model-nodes.tsx b/src/pages/dashboard/components/model-nodes.tsx index 4aa7d72c..30474950 100644 --- a/src/pages/dashboard/components/model-nodes.tsx +++ b/src/pages/dashboard/components/model-nodes.tsx @@ -1,7 +1,7 @@ import PageTools from '@/components/page-tools'; import StatusTag from '@/components/status-tag'; import useTableRowSelection from '@/hooks/use-table-row-selection'; -import { NodeItem } from '@/pages/nodes/config/types'; +import { NodeItem } from '@/pages/resources/config/types'; import { Progress, Select, Space, Table } from 'antd'; import _ from 'lodash'; import { memo, useMemo, useState } from 'react'; diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index e20680cf..d880fcee 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -3,8 +3,6 @@ import PageTools from '@/components/page-tools'; import { PageContainer } from '@ant-design/pro-components'; import { Col, Row } from 'antd'; import GPUUtilization from './components/gpu-utilization'; -import ModelBills from './components/model-bills'; -import ModelNodes from './components/model-nodes'; import Overview from './components/over-view'; import UtilizationOvertime from './components/utilization-overtime'; @@ -17,10 +15,26 @@ const Dashboard: React.FC = () => { - - + + + VRAM + + } + > + - + { - + {/*
-
+ */}
diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index a49a7ba3..c302e4c5 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -5,7 +5,9 @@ import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableSort from '@/hooks/use-table-sort'; import { DeleteOutlined, + DownOutlined, PlusOutlined, + RightOutlined, SyncOutlined, WechatWorkOutlined } from '@ant-design/icons'; @@ -192,6 +194,17 @@ const Models: React.FC = () => { onShowSizeChange: handleShowSizeChange, onChange: handlePageChange }} + expandable={{ + expandIcon: ({ expanded, onExpand, record }) => { + return expanded ? ( + onExpand(record, e)} /> + ) : ( + onExpand(record, e)} /> + ); + }, + expandedRowRender: (record) =>

list

, + rowExpandable: (record) => record.name !== 'Not Expandable' + }} > { - const rowSelection = useTableRowSelection(); - const { sortOrder, setSortOrder } = useTableSort({ - defaultSortOrder: 'descend' - }); - const [total, setTotal] = useState(10); - const [loading, setLoading] = useState(false); - const [queryParams, setQueryParams] = useState({ - current: 1, - pageSize: 10, - name: '' - }); - const handleShowSizeChange = (current: number, size: number) => { - console.log(current, size); - }; - - const handlePageChange = (page: number, pageSize: number | undefined) => { - console.log(page, pageSize); - }; - - const handleTableChange = (pagination: any, filters: any, sorter: any) => { - setSortOrder(sorter.order); - }; - - const fetchData = async () => { - console.log('fetchData'); - }; - const handleSearch = (e: any) => { - fetchData(); - }; - - const handleNameChange = (e: any) => { - setQueryParams({ - ...queryParams, - name: e.target.value - }); - }; - - return ( - <> - - - - - - } - > - - - { - return ( - - ); - }} - /> - - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - - - ); - }} - /> -
-
- - ); -}; - -export default Models; diff --git a/src/pages/playground/components/ground-left.tsx b/src/pages/playground/components/ground-left.tsx index d75ef4e0..63924df6 100644 --- a/src/pages/playground/components/ground-left.tsx +++ b/src/pages/playground/components/ground-left.tsx @@ -1,7 +1,10 @@ -import SealInput from '@/components/seal-form/seal-input'; +import TransitionWrapper from '@/components/transition'; +import { EyeInvisibleOutlined } from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-components'; +import { Button, Input } from 'antd'; import { useState } from 'react'; import '../style/ground-left.less'; +import '../style/system-message-wrap.less'; import ChatFooter from './chat-footer'; import MessageItem from './message-item'; import ReferenceParams from './reference-params'; @@ -18,9 +21,14 @@ const MessageList: React.FC = () => { message: 'hello, nice to meet you!' } ]); + const [systemMessage, setSystemMessage] = useState(''); const [show, setShow] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); + const handleSystemMessageChange = (e: any) => { + console.log('system message:', e.target.value); + setSystemMessage(e.target.value); + }; const handleNewMessage = () => { console.log('new message'); messageList.push({ @@ -52,12 +60,30 @@ const MessageList: React.FC = () => { setMessageList([...messageList]); }; + const renderLabel = () => { + return ( +
+ System + +
+ ); + }; return (
-
- +
+ + +
+
{messageList.map((item, index) => { return ( diff --git a/src/pages/playground/components/message-item.tsx b/src/pages/playground/components/message-item.tsx index 2abc9759..5fa62884 100644 --- a/src/pages/playground/components/message-item.tsx +++ b/src/pages/playground/components/message-item.tsx @@ -57,7 +57,7 @@ const MessageContent: React.FC<{ diff --git a/src/pages/playground/style/message-item.less b/src/pages/playground/style/message-item.less index c500d050..4a7d46c6 100644 --- a/src/pages/playground/style/message-item.less +++ b/src/pages/playground/style/message-item.less @@ -1,16 +1,36 @@ .message-item { + position: relative; display: flex; justify-content: flex-start; align-items: flex-start; margin-bottom: 12px; + &:hover { + .delete-btn { + opacity: 1; + transition: all 0.3s var(--seal-transition-func); + } + } .role-type { margin-right: 12px; + background-color: var(--ant-button-text-hover-bg); .ant-btn { text-align: left; width: 100px; + background-color: var(--ant-button-text-hover-bg); + height: 46px; } } .message-content-input { flex: 1; + .ant-input { + padding-right: 30px; + } + } + .delete-btn { + opacity: 0; + position: absolute; + top: 2px; + right: 0; + transition: all 0.3s var(--seal-transition-func); } } diff --git a/src/pages/playground/style/system-message-wrap.less b/src/pages/playground/style/system-message-wrap.less new file mode 100644 index 00000000..bc29dc80 --- /dev/null +++ b/src/pages/playground/style/system-message-wrap.less @@ -0,0 +1,5 @@ +.system-message-wrap { + display: flex; + justify-content: space-between; + align-items: center; +}