| @@ -64,4 +64,4 @@ mvnw | |||
| /react-ui/docs | |||
| /react-ui/types/tsconfig.tsbuildinfo | |||
| /react-ui/storybook-static | |||
| /react-ui/.storybook/deploy.sh | |||
| /react-ui/.storybook/scripts | |||
| @@ -0,0 +1,3 @@ | |||
| # Dockerfile | |||
| FROM nginx:alpine | |||
| COPY storybook-static/ /usr/share/nginx/html | |||
| @@ -40,7 +40,7 @@ | |||
| "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", | |||
| "storybook": "storybook dev -p 6006", | |||
| "storybook-build": "storybook build", | |||
| "storybook-deploy": "./.storybook/deploy.sh", | |||
| "storybook-deploy": "./.storybook/scripts/upload-deploy.sh", | |||
| "storybook-docs": "storybook dev --docs", | |||
| "storybook-docs-build": "storybook build --docs", | |||
| "test": "jest", | |||
| @@ -137,7 +137,6 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||
| onClick: () => { | |||
| // 点击菜单项,删除所有的页面 state 缓存 | |||
| removeAllPageCacheState(); | |||
| // console.log('click menu'); | |||
| }, | |||
| }, | |||
| ...initialState?.settings, | |||
| @@ -3,7 +3,7 @@ import { setRemoteMenu } from '@/services/session'; | |||
| import { logout } from '@/services/system/auth'; | |||
| import { ClientInfo } from '@/types'; | |||
| import SessionStorage from '@/utils/sessionStorage'; | |||
| import { gotoLoginPage } from '@/utils/ui'; | |||
| import { gotoLoginPage, oauthLogout } from '@/utils/ui'; | |||
| import { LogoutOutlined, UserOutlined } from '@ant-design/icons'; | |||
| import { setAlpha } from '@ant-design/pro-components'; | |||
| import { useEmotionCss } from '@ant-design/use-emotion-css'; | |||
| @@ -62,6 +62,7 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => { | |||
| * 退出登录,并且将当前的 url 保存 | |||
| */ | |||
| const loginOut = async () => { | |||
| oauthLogout('http://172.20.32.197:31209/oauth/logout'); | |||
| await logout(); | |||
| clearSessionToken(); | |||
| setRemoteMenu(null); | |||
| @@ -160,3 +160,8 @@ ol { | |||
| input:-webkit-autofill { | |||
| transition: background-color 5000s ease-in-out 0s; | |||
| } | |||
| .ant-typography { | |||
| color: inherit; | |||
| font-size: inherit; | |||
| } | |||
| @@ -261,8 +261,3 @@ | |||
| } | |||
| } | |||
| } | |||
| .ant-typography { | |||
| color: inherit; | |||
| font-size: inherit; | |||
| } | |||
| @@ -104,16 +104,16 @@ function EditorCreate() { | |||
| <Row gutter={10}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="任务名称" | |||
| label="编辑器名称" | |||
| name="name" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入任务名称', | |||
| message: '请输入编辑器名称', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input placeholder="请输入任务名称" maxLength={64} showCount allowClear /> | |||
| <Input placeholder="请输入编辑器名称" maxLength={64} showCount allowClear /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| @@ -7,6 +7,8 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { DevEditorStatus } from '@/enums'; | |||
| import { useCacheState } from '@/hooks/useCacheState'; | |||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||
| import { DatasetData, ModelData } from '@/pages/Dataset/config'; | |||
| import { | |||
| deleteEditorReq, | |||
| getEditorListReq, | |||
| @@ -14,6 +16,7 @@ import { | |||
| stopEditorReq, | |||
| } from '@/services/developmentEnvironment'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { parseJsonText } from '@/utils'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| import SessionStorage from '@/utils/sessionStorage'; | |||
| @@ -42,6 +45,10 @@ export type EditorData = { | |||
| update_by: string; | |||
| create_time: string; | |||
| url: string; | |||
| computing_resource_id: number; | |||
| dataset?: string | DatasetData; | |||
| model?: string | ModelData; | |||
| image?: string; | |||
| }; | |||
| function EditorList() { | |||
| @@ -56,6 +63,7 @@ function EditorList() { | |||
| pageSize: 10, | |||
| }, | |||
| ); | |||
| const getResourceDescription = useComputingResource()[1]; | |||
| // 获取编辑器列表 | |||
| const getEditorList = useCallback(async () => { | |||
| @@ -66,6 +74,10 @@ function EditorList() { | |||
| const [res] = await to(getEditorListReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| content.forEach((item: EditorData) => { | |||
| item.dataset = typeof item.dataset === 'string' ? parseJsonText(item.dataset) : null; | |||
| item.model = typeof item.model === 'string' ? parseJsonText(item.model) : null; | |||
| }); | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| @@ -102,11 +114,18 @@ function EditorList() { | |||
| // 停止编辑器 | |||
| const stopEditor = async (id: number) => { | |||
| const [res] = await to(stopEditorReq(id)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| getEditorList(); | |||
| } | |||
| modalConfirm({ | |||
| title: '停止后,该编辑器将不可使用', | |||
| content: '是否确认停止?', | |||
| isDelete: false, | |||
| onOk: async () => { | |||
| const [res] = await to(stopEditorReq(id)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| getEditorList(); | |||
| } | |||
| }, | |||
| }); | |||
| }; | |||
| // 制作镜像 | |||
| @@ -165,44 +184,72 @@ function EditorList() { | |||
| title: '编辑器名称', | |||
| dataIndex: 'name', | |||
| key: 'name', | |||
| width: '30%', | |||
| render: (text, record) => | |||
| record.url && record.status === DevEditorStatus.Running ? ( | |||
| <a className="kf-table-row-link" onClick={(e) => gotoEditorPage(e, record)}> | |||
| {text} | |||
| </a> | |||
| ) : ( | |||
| <span>{text ?? '--'}</span> | |||
| ), | |||
| }, | |||
| { | |||
| title: '状态', | |||
| dataIndex: 'status', | |||
| key: 'status', | |||
| width: '10%', | |||
| render: EditorStatusCell, | |||
| width: '20%', | |||
| render: (text, record, index) => | |||
| record.url && record.status === DevEditorStatus.Running | |||
| ? tableCellRender<EditorData>(true, TableCellValueType.Link, { | |||
| onClick: (record, e) => gotoEditorPage(e, record), | |||
| })(text, record, index) | |||
| : tableCellRender<EditorData>(true, TableCellValueType.Text)(text, record, index), | |||
| }, | |||
| { | |||
| title: '资源', | |||
| title: '计算资源', | |||
| dataIndex: 'computing_resource', | |||
| key: 'computing_resource', | |||
| width: '20%', | |||
| width: 100, | |||
| render: tableCellRender(), | |||
| }, | |||
| { | |||
| title: '资源规格', | |||
| dataIndex: 'computing_resource_id', | |||
| key: 'computing_resource_id', | |||
| width: '20%', | |||
| render: tableCellRender(true, TableCellValueType.Custom, { | |||
| format: getResourceDescription, | |||
| }), | |||
| }, | |||
| { | |||
| title: '数据集', | |||
| dataIndex: ['dataset', 'showValue'], | |||
| key: 'dataset', | |||
| width: '15%', | |||
| render: tableCellRender(true), | |||
| }, | |||
| { | |||
| title: '模型', | |||
| dataIndex: ['model', 'showValue'], | |||
| key: 'model', | |||
| width: '15%', | |||
| render: tableCellRender(true), | |||
| }, | |||
| { | |||
| title: '镜像', | |||
| dataIndex: ['image'], | |||
| key: 'image', | |||
| width: '15%', | |||
| render: tableCellRender(true), | |||
| }, | |||
| { | |||
| title: '创建者', | |||
| dataIndex: 'update_by', | |||
| key: 'update_by', | |||
| width: '20%', | |||
| render: tableCellRender(), | |||
| width: '15%', | |||
| render: tableCellRender(true), | |||
| }, | |||
| { | |||
| title: '创建时间', | |||
| dataIndex: 'create_time', | |||
| key: 'create_time', | |||
| width: '20%', | |||
| width: 180, | |||
| render: tableCellRender(false, TableCellValueType.Date), | |||
| }, | |||
| { | |||
| title: '状态', | |||
| dataIndex: 'status', | |||
| key: 'status', | |||
| width: 80, | |||
| render: EditorStatusCell, | |||
| }, | |||
| { | |||
| title: '操作', | |||
| dataIndex: 'operation', | |||
| @@ -20,7 +20,7 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) { | |||
| }), | |||
| ); | |||
| if (res) { | |||
| message.success('创建成功,请到 “AI资产” - “个人镜像” 中查看'); | |||
| message.success('创建成功,请到 “多形态资源库” - “个人镜像” 中查看'); | |||
| onOk?.(); | |||
| } | |||
| }; | |||
| @@ -59,12 +59,12 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) { | |||
| <Input placeholder="请输入镜像名称" maxLength={64} showCount allowClear /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="镜像Tag" | |||
| label="镜像版本" | |||
| name="tag_name" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入镜像Tag', | |||
| message: '请输入镜像版本', | |||
| }, | |||
| { | |||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||
| @@ -72,7 +72,7 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) { | |||
| }, | |||
| ]} | |||
| > | |||
| <Input placeholder="请输入镜像Tag" maxLength={64} showCount allowClear /> | |||
| <Input placeholder="请输入镜像版本" maxLength={64} showCount allowClear /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="镜像描述" | |||
| @@ -87,7 +87,7 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) { | |||
| <Input.TextArea | |||
| placeholder="请输入镜像描述" | |||
| autoSize={{ minRows: 3, maxRows: 6 }} | |||
| maxLength={256} | |||
| maxLength={128} | |||
| showCount | |||
| allowClear | |||
| /> | |||
| @@ -6,14 +6,13 @@ | |||
| flex-direction: column; | |||
| height: calc(100% - 60px); | |||
| margin-top: 10px; | |||
| padding: 30px 30px 0; | |||
| padding: 10px 30px 0; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__tabs { | |||
| flex: 1; | |||
| min-height: 0; | |||
| margin-top: 20px; | |||
| padding-bottom: 10px; | |||
| :global { | |||
| @@ -3,9 +3,9 @@ | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 服务版本详情 | |||
| */ | |||
| import FullScreenFrame from '@/components/FullScreenFrame'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { getServiceVersionInfoReq } from '@/services/modelDeployment'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useParams } from '@umijs/max'; | |||
| @@ -18,6 +18,7 @@ import { ServiceVersionData } from '../types'; | |||
| import styles from './index.less'; | |||
| export enum ModelDeploymentTabKey { | |||
| Basic = 'Basic', // 基本信息 | |||
| Predict = 'Predict', // 预测 | |||
| Guide = 'Guide', // 调用指南 | |||
| Log = 'Log', // 服务日志 | |||
| @@ -43,10 +44,23 @@ function ServiceVersionInfo() { | |||
| }, [id]); | |||
| const tabItems = [ | |||
| { | |||
| key: ModelDeploymentTabKey.Basic, | |||
| label: '基本信息', | |||
| icon: <KFIcon type="icon-jibenxinxi" />, | |||
| children: <VersionBasicInfo info={versionInfo} />, | |||
| }, | |||
| { | |||
| key: ModelDeploymentTabKey.Predict, | |||
| label: '预测', | |||
| icon: <KFIcon type="icon-yuce" />, | |||
| children: ( | |||
| <div style={{ height: '100%', width: '100%' }}> | |||
| {versionInfo?.page_path && ( | |||
| <FullScreenFrame url={versionInfo?.page_path}></FullScreenFrame> | |||
| )} | |||
| </div> | |||
| ), | |||
| }, | |||
| { | |||
| key: ModelDeploymentTabKey.Guide, | |||
| @@ -66,12 +80,6 @@ function ServiceVersionInfo() { | |||
| <div className={styles['service-version-info']}> | |||
| <PageTitle title="服务版本详情"></PageTitle> | |||
| <div className={styles['service-version-info__content']}> | |||
| <SubAreaTitle | |||
| title="基本信息" | |||
| image={require('@/assets/img/mirror-basic.png')} | |||
| style={{ marginBottom: '26px' }} | |||
| ></SubAreaTitle> | |||
| <VersionBasicInfo info={versionInfo} /> | |||
| <div className={styles['service-version-info__content__tabs']}> | |||
| <Tabs items={tabItems} /> | |||
| </div> | |||
| @@ -79,6 +79,10 @@ function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| label: 'API URL', | |||
| value: info?.url, | |||
| }, | |||
| { | |||
| label: '文档地址', | |||
| value: info?.doc_path, | |||
| }, | |||
| { | |||
| label: '副本数量', | |||
| value: info?.replicas, | |||
| @@ -104,7 +108,14 @@ function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| }, | |||
| ]; | |||
| return <BasicInfo datas={datas} labelWidth={66} labelAlign="justify"></BasicInfo>; | |||
| return ( | |||
| <BasicInfo | |||
| datas={datas} | |||
| labelWidth={66} | |||
| labelAlign="justify" | |||
| style={{ marginTop: 10 }} | |||
| ></BasicInfo> | |||
| ); | |||
| } | |||
| export default VersionBasicInfo; | |||
| @@ -49,6 +49,8 @@ export type ServiceVersionData = { | |||
| update_time: string; | |||
| create_time: string; | |||
| created_by: string; | |||
| doc_path?: string; // 文档地址 | |||
| page_path?: string; // 预测地址 | |||
| }; | |||
| // 操作类型 | |||
| @@ -188,7 +188,7 @@ function PointsDetail() { | |||
| render: tableCellRender(), | |||
| }, | |||
| { | |||
| title: '描述', | |||
| title: '资源规格', | |||
| dataIndex: 'description', | |||
| key: 'description', | |||
| render: tableCellRender(true), | |||
| @@ -217,7 +217,7 @@ function PointsDetail() { | |||
| color: themes['primaryColor'], | |||
| }, | |||
| { | |||
| value: 0, | |||
| value: 2, | |||
| label: '准备中', | |||
| color: themes['pendingColor'], | |||
| }, | |||
| @@ -316,11 +316,19 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| label="算力积分" | |||
| placeholder="请输入算力积分" | |||
| colProps={{ xs: 24, md: 12, xl: 12 }} | |||
| max={100000} | |||
| min={0} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入算力积分', | |||
| }, | |||
| { | |||
| type: 'number', | |||
| min: 0, | |||
| max: 100000, | |||
| message: '请输入0 ~ 100000之间的数', | |||
| }, | |||
| ]} | |||
| /> | |||
| <ProFormTextArea | |||
| @@ -3,9 +3,9 @@ | |||
| flex: none; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| width: 326px; | |||
| height: 228px; | |||
| padding: 0 20px; | |||
| .backgroundFullImage(url(@/assets/img/user-points-bg.png)); | |||
| &__label { | |||
| @@ -16,12 +16,15 @@ | |||
| } | |||
| &__value { | |||
| width: 100%; | |||
| margin-top: 8px; | |||
| margin-bottom: 12px; | |||
| color: @primary-color; | |||
| font-size: 36px; | |||
| font-family: DingTalk-JinBuTi; | |||
| line-height: 43px; | |||
| text-align: center; | |||
| .singleLine(); | |||
| } | |||
| &__button { | |||
| @@ -2,6 +2,7 @@ import { PointsStatistics } from '@/pages/Points/index'; | |||
| import { getPointsStatisticsReq } from '@/services/points'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { Typography } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| @@ -23,8 +24,13 @@ function UserPoints() { | |||
| return ( | |||
| <div className={styles['user-points']}> | |||
| <span className={styles['user-points__label']}>当前可用算力积分</span> | |||
| <span className={styles['user-points__value']}>{statistics?.userCredit ?? '--'}</span> | |||
| <div className={styles['user-points__label']}>当前可用算力积分</div> | |||
| <Typography.Paragraph | |||
| className={styles['user-points__value']} | |||
| ellipsis={{ tooltip: statistics?.userCredit ?? '--' }} | |||
| > | |||
| {statistics?.userCredit ?? '--'} | |||
| </Typography.Paragraph> | |||
| <div | |||
| className={styles['user-points__button']} | |||
| onClick={() => { | |||
| @@ -226,3 +226,14 @@ export const removeFormListItem = ( | |||
| }, | |||
| }); | |||
| }; | |||
| /** | |||
| * 退出子系统 | |||
| * @param url - 退出登录的地址 | |||
| */ | |||
| export const oauthLogout = (url: string) => { | |||
| const iframe = document.createElement('iframe'); | |||
| iframe.style.display = 'none'; | |||
| iframe.src = url; | |||
| document.body.appendChild(iframe); | |||
| }; | |||