| @@ -345,7 +345,6 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: '应用开发', | |||
| path: '/appsDeployment', | |||
| @@ -498,6 +497,18 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: '算力积分', | |||
| path: '/points', | |||
| routes: [ | |||
| { | |||
| name: '算力积分', | |||
| path: '', | |||
| key: 'points', | |||
| component: './Points/index', | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| path: '*', | |||
| layout: false, | |||
| @@ -1,5 +1,23 @@ | |||
| @font-face { | |||
| font-family: Alibaba; | |||
| src: url('./ALIBABA-PUHUITI-MEDIUM.TTF'); | |||
| font-display: swap; | |||
| } | |||
| font-family: Alibaba; | |||
| src: url('./ALIBABA-PUHUITI-MEDIUM.TTF'); | |||
| font-display: swap; | |||
| } | |||
| @font-face { | |||
| font-family: 'TaoBaoMaiCaiTi'; | |||
| src: url('./TaoBaoMaiCaiTi-Regular.woff2') format('woff2'), /* 最优先使用 woff2 */ | |||
| url('./TaoBaoMaiCaiTi-Regular.woff') format('woff'), /* 兼容性较好的 woff */ | |||
| url('./TaoBaoMaiCaiTi-Regular.ttf') format('truetype'), /* ttf 作为备选 */ | |||
| url('./TaoBaoMaiCaiTi-Regular.otf') format('opentype'); /* otf 作为最后选项 */ | |||
| font-display: swap; /* 优化页面加载时的字体显示 */ | |||
| } | |||
| @font-face { | |||
| font-family: 'DingTalk-JinBuTi'; | |||
| src: url('./DingTalk-JinBuTi.woff2') format('woff2'), /* 最优先使用 woff2 */ | |||
| url('./DingTalk-JinBuTi.woff') format('woff'), /* 兼容性较好的 woff */ | |||
| url('./DingTalk-JinBuTi.ttf') format('truetype'); /* ttf 作为备选 */ | |||
| font-display: swap; /* 优化页面加载时的字体显示 */ | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| .statistics { | |||
| display: flex; | |||
| align-items: center; | |||
| width: 100%; | |||
| padding: 20px 0; | |||
| background-color: @background-color; | |||
| border-radius: 8px; | |||
| &__item { | |||
| display: flex; | |||
| flex: 1; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| &--border { | |||
| border-right: 1px solid @border-color; | |||
| } | |||
| &__value { | |||
| margin-bottom: 8px; | |||
| color: @text-color; | |||
| font-weight: bold; | |||
| font-size: 30px; | |||
| } | |||
| &__title { | |||
| color: #999999; | |||
| font-size: @font-size-input; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| import classNames from 'classnames'; | |||
| import styles from './index.less'; | |||
| type StatisticsProps = { | |||
| remaining?: number; | |||
| consuming?: number; | |||
| }; | |||
| function Statistics({ remaining, consuming }: StatisticsProps) { | |||
| const items = [ | |||
| { | |||
| title: '当前可用算力积分(分)', | |||
| value: remaining ?? '-', | |||
| }, | |||
| { | |||
| title: '总消耗算力积分(分)', | |||
| value: consuming ?? '-', | |||
| }, | |||
| ]; | |||
| return ( | |||
| <div className={styles['statistics']}> | |||
| {items.map((item, index) => ( | |||
| <div | |||
| key={item.title} | |||
| className={classNames(styles['statistics__item'], { | |||
| [styles['statistics__item--border']]: index === 0, | |||
| })} | |||
| > | |||
| <span className={styles['statistics__item__value']}>{item.value}</span> | |||
| <span className={styles['statistics__item__title']}>{item.title}</span> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| ); | |||
| } | |||
| export default Statistics; | |||
| @@ -0,0 +1,19 @@ | |||
| .points-detail { | |||
| height: 100%; | |||
| &__content { | |||
| height: calc(100% - 60px); | |||
| margin-top: 10px; | |||
| padding: 20px 30px 0; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__top { | |||
| width: 100%; | |||
| } | |||
| &__table { | |||
| height: calc(100% - 117px - 28px); | |||
| margin-top: 28px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,256 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 模型部署服务列表 | |||
| */ | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import { getPointsConsumptionReq, getPointsStatisticsReq } from '@/services/points'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { to } from '@/utils/promise'; | |||
| import statusTableCell from '@/utils/statusTableCell'; | |||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | |||
| import { Link } from '@umijs/max'; | |||
| import { Table, type TablePaginationConfig, type TableProps } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import Statistics from './components/Statistics'; | |||
| import styles from './index.less'; | |||
| enum TaskType { | |||
| DevEnvironment = 'dev_environment', | |||
| Workflow = 'workflow', | |||
| Ray = 'ray', | |||
| Service = 'service', | |||
| } | |||
| const taskTypeOptions = [ | |||
| { | |||
| value: 'dev_environment', | |||
| label: '开发环境', | |||
| }, | |||
| { | |||
| value: 'workflow', | |||
| label: '实验', | |||
| }, | |||
| { | |||
| value: 'ray', | |||
| label: '超参数自动寻优', | |||
| }, | |||
| { | |||
| value: 'service', | |||
| label: '服务', | |||
| }, | |||
| ]; | |||
| export type PointsData = { | |||
| computing_resource_id: number; | |||
| credit_per_hour: number; // 每小时消耗的积分 | |||
| deduce_credit: number; | |||
| deduce_last_time: string; | |||
| description: string; | |||
| id: number; | |||
| node_id: string; | |||
| start_time: string; | |||
| state: number; | |||
| workflow_id?: number; // 流水线id | |||
| task_id: number; // 实验id | |||
| task_ins_id: number; // 实例id | |||
| task_type: string; | |||
| user_id: number; | |||
| }; | |||
| export type PointsStatistics = { | |||
| deduceCredit: number; | |||
| userCredit: number; | |||
| }; | |||
| // 格式化任务 | |||
| const formatTask = (value: string, record: PointsData) => { | |||
| let url; | |||
| switch (record.task_type) { | |||
| case TaskType.DevEnvironment: | |||
| url = `/developmentEnvironment`; | |||
| break; | |||
| case TaskType.Workflow: | |||
| if (record.workflow_id && record.task_ins_id) { | |||
| url = `/pipeline/experiment/instance/${record.workflow_id}/${record.task_ins_id}`; | |||
| } | |||
| break; | |||
| case TaskType.Ray: | |||
| if (record.task_id && record.task_ins_id) { | |||
| url = `/pipeline/hyperparameter/instance/${record.task_id}/${record.task_ins_id}`; | |||
| } | |||
| break; | |||
| case TaskType.Service: | |||
| if (record.task_id && record.task_ins_id) { | |||
| url = `/dataset/modelDeployment/serviceInfo/${record.task_id}/versionInfo/${record.task_ins_id}`; | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| if (url) { | |||
| return <Link to={url}>{value}</Link>; | |||
| } else { | |||
| return <span>{value}</span>; | |||
| } | |||
| }; | |||
| function PointsDetail() { | |||
| const [tableData, setTableData] = useState<PointsData[]>([]); | |||
| const [total, setTotal] = useState(0); | |||
| const [statistics, setStatistics] = useState<PointsStatistics>(); | |||
| const [pagination, setPagination] = useState<TablePaginationConfig>({ | |||
| current: 1, | |||
| pageSize: 10, | |||
| }); | |||
| useEffect(() => { | |||
| // 获取积分统计 | |||
| const getPointsStatistics = async () => { | |||
| const [res] = await to(getPointsStatisticsReq()); | |||
| if (res && res.data) { | |||
| setStatistics(res.data); | |||
| } | |||
| }; | |||
| getPointsStatistics(); | |||
| }, []); | |||
| useEffect(() => { | |||
| // 获取积分消费明细 | |||
| const getPointsConsumption = async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| }; | |||
| const [res] = await to(getPointsConsumptionReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| getPointsConsumption(); | |||
| }, [pagination]); | |||
| // 分页切换 | |||
| const handleTableChange: TableProps<PointsData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| }; | |||
| const columns: TableProps<PointsData>['columns'] = [ | |||
| { | |||
| title: '序号', | |||
| dataIndex: 'index', | |||
| key: 'index', | |||
| width: 80, | |||
| render: tableCellRender(false, TableCellValueType.Index, { | |||
| page: pagination.current! - 1, | |||
| pageSize: pagination.pageSize!, | |||
| }), | |||
| }, | |||
| { | |||
| title: '任务', | |||
| dataIndex: 'task_name', | |||
| key: 'task_name', | |||
| width: '15%', | |||
| render: formatTask, | |||
| }, | |||
| { | |||
| title: '任务类型', | |||
| dataIndex: 'task_type', | |||
| key: 'task_type', | |||
| width: '10%', | |||
| render: statusTableCell(taskTypeOptions), | |||
| }, | |||
| { | |||
| title: '积分/小时', | |||
| dataIndex: 'credit_per_hour', | |||
| key: 'credit_per_hour', | |||
| width: '10%', | |||
| render: tableCellRender(), | |||
| }, | |||
| { | |||
| title: '消耗的积分(分)', | |||
| dataIndex: 'deduce_credit', | |||
| key: 'deduce_credit', | |||
| width: '12%', | |||
| render: tableCellRender(), | |||
| }, | |||
| { | |||
| title: '描述', | |||
| dataIndex: 'description', | |||
| key: 'description', | |||
| render: tableCellRender(true), | |||
| }, | |||
| { | |||
| title: '开始时间', | |||
| dataIndex: 'start_time', | |||
| key: 'start_time', | |||
| width: '15%', | |||
| render: tableCellRender(false, TableCellValueType.Date), | |||
| }, | |||
| { | |||
| title: '状态', | |||
| dataIndex: 'state', | |||
| key: 'state', | |||
| width: '8%', | |||
| render: statusTableCell([ | |||
| { | |||
| value: 0, | |||
| label: '已结束', | |||
| color: themes['textColor'], | |||
| }, | |||
| { | |||
| value: 1, | |||
| label: '进行中', | |||
| color: themes['primaryColor'], | |||
| }, | |||
| ]), | |||
| }, | |||
| ]; | |||
| return ( | |||
| <div className={styles['points-detail']}> | |||
| <PageTitle title="算力积分明细"></PageTitle> | |||
| <div className={styles['points-detail__content']}> | |||
| <div className={styles['points-detail__content__top']}> | |||
| <Statistics | |||
| remaining={statistics?.userCredit} | |||
| consuming={statistics?.deduceCredit} | |||
| ></Statistics> | |||
| </div> | |||
| <div | |||
| className={classNames('vertical-scroll-table', styles['points-detail__content__table'])} | |||
| > | |||
| <Table | |||
| dataSource={tableData} | |||
| columns={columns} | |||
| scroll={{ y: 'calc(100% - 55px)' }} | |||
| pagination={{ | |||
| ...pagination, | |||
| total: total, | |||
| showSizeChanger: true, | |||
| showQuickJumper: true, | |||
| showTotal: () => `共${total}条`, | |||
| }} | |||
| onChange={handleTableChange} | |||
| rowKey="id" | |||
| /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default PointsDetail; | |||
| @@ -2,6 +2,7 @@ import { DictValueEnumObj } from '@/components/DictTag'; | |||
| import KFModal from '@/components/KFModal'; | |||
| import { | |||
| ProForm, | |||
| ProFormDigit, | |||
| ProFormRadio, | |||
| ProFormSelect, | |||
| ProFormText, | |||
| @@ -64,6 +65,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| remark: props.values.remark, | |||
| gitLinkUsername: props.values.gitLinkUsername, | |||
| gitLinkPassword: props.values.gitLinkPassword, | |||
| credit: props.values.credit, | |||
| }); | |||
| }, [form, props, statusOptions]); | |||
| @@ -219,12 +221,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| autoComplete: 'new-password', | |||
| }} | |||
| allowClear | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: <FormattedMessage id="请输入密码!" defaultMessage="请输入密码!" />, | |||
| }, | |||
| ]} | |||
| rules={props.values.userId ? [] : [{ required: true, message: '请输入密码!' }]} | |||
| /> | |||
| <ProFormSelect | |||
| valueEnum={sexOptions} | |||
| @@ -304,6 +301,18 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| }} | |||
| rules={props.values.userId ? [] : [{ required: true, message: '请输入 Git 密码!' }]} | |||
| /> | |||
| <ProFormDigit | |||
| name="credit" | |||
| label="算力积分" | |||
| placeholder="请输入算力积分" | |||
| colProps={{ xs: 24, md: 12, xl: 12 }} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入算力积分', | |||
| }, | |||
| ]} | |||
| /> | |||
| <ProFormTextArea | |||
| name="remark" | |||
| label={intl.formatMessage({ | |||
| @@ -259,6 +259,11 @@ const UserTableList: React.FC = () => { | |||
| dataIndex: 'phonenumber', | |||
| valueType: 'text', | |||
| }, | |||
| { | |||
| title: <FormattedMessage id="system.user.credit" defaultMessage="算力积分" />, | |||
| dataIndex: 'credit', | |||
| valueType: 'text', | |||
| }, | |||
| { | |||
| title: <FormattedMessage id="system.user.status" defaultMessage="帐号状态" />, | |||
| dataIndex: 'status', | |||
| @@ -1,4 +1,5 @@ | |||
| .experiment-chart { | |||
| flex: none; | |||
| width: 295px; | |||
| min-width: 295px; | |||
| height: 140px; | |||
| @@ -1,8 +1,8 @@ | |||
| .experiment-table { | |||
| flex: 1; | |||
| min-width: 500px; | |||
| min-width: 378px; | |||
| height: 140px; | |||
| padding: 12px 24px; | |||
| padding: 12px; | |||
| background: @workspace-background; | |||
| border-radius: 4px; | |||
| @@ -32,15 +32,15 @@ | |||
| } | |||
| &__status { | |||
| width: 20%; | |||
| width: 15%; | |||
| } | |||
| &__duration { | |||
| width: 30%; | |||
| width: 25%; | |||
| } | |||
| &__date { | |||
| width: calc(50% - 60px); | |||
| width: calc(60% - 60px); | |||
| } | |||
| &__operation { | |||
| @@ -1,16 +1,12 @@ | |||
| .total-statistics { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 400px; | |||
| height: 140px; | |||
| background: @workspace-background; | |||
| border-radius: 4px; | |||
| padding: 0 16px; | |||
| &__icon { | |||
| width: 85px; | |||
| height: 80px; | |||
| margin-right: 40px; | |||
| width: 63px; | |||
| margin-right: 15px; | |||
| } | |||
| &__title { | |||
| @@ -20,18 +16,10 @@ | |||
| font-size: @font-size-content; | |||
| } | |||
| &__title-shadow { | |||
| position: absolute; | |||
| bottom: 6px; | |||
| left: 0; | |||
| width: 79px; | |||
| height: 6px; | |||
| background: linear-gradient(87.07deg, rgba(22, 100, 255, 0.6) 0%, rgba(22, 100, 255, 0) 100%); | |||
| } | |||
| &__count { | |||
| color: @text-color; | |||
| font-weight: 700; | |||
| font-size: 25px; | |||
| font-family: DingTalk-JinBuTi; | |||
| } | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| import { Flex } from 'antd'; | |||
| import styles from './index.less'; | |||
| type TotalStatisticsProps = { | |||
| @@ -11,13 +12,12 @@ function TotalStatistics({ icon = '', title = '', count = 0, style }: TotalStati | |||
| return ( | |||
| <div className={styles['total-statistics']} style={style}> | |||
| <img className={styles['total-statistics__icon']} src={icon} draggable={false} alt="" /> | |||
| <div> | |||
| <Flex vertical align="center"> | |||
| <div className={styles['total-statistics__title']}> | |||
| <span>{title}</span> | |||
| <div className={styles['total-statistics__title-shadow']}></div> | |||
| </div> | |||
| <div className={styles['total-statistics__count']}>{count ?? '--'}</div> | |||
| </div> | |||
| </Flex> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| .user-points { | |||
| display: flex; | |||
| flex: none; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| width: 326px; | |||
| height: 228px; | |||
| .backgroundFullImage(url(@/assets/img/user-points-bg.png)); | |||
| &__label { | |||
| margin-top: 60px; | |||
| color: @text-color; | |||
| font-size: @font-size-title; | |||
| font-family: DingTalk-JinBuTi; | |||
| } | |||
| &__value { | |||
| margin-top: 8px; | |||
| margin-bottom: 12px; | |||
| color: @primary-color; | |||
| font-size: 36px; | |||
| font-family: DingTalk-JinBuTi; | |||
| line-height: 43px; | |||
| } | |||
| &__button { | |||
| padding: 8px 20px; | |||
| color: @primary-color; | |||
| font-size: @font-size-content; | |||
| background: rgba(255, 255, 255, 0.3); | |||
| border: 1px solid #ffffff; | |||
| border-radius: 8px; | |||
| cursor: pointer; | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| import { PointsStatistics } from '@/pages/Points/index'; | |||
| import { getPointsStatisticsReq } from '@/services/points'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| function UserPoints() { | |||
| const [statistics, setStatistics] = useState<PointsStatistics>(); | |||
| const navigate = useNavigate(); | |||
| useEffect(() => { | |||
| // 获取积分统计 | |||
| const getPointsStatistics = async () => { | |||
| const [res] = await to(getPointsStatisticsReq()); | |||
| if (res && res.data) { | |||
| setStatistics(res.data); | |||
| } | |||
| }; | |||
| getPointsStatistics(); | |||
| }, []); | |||
| 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__button']} | |||
| onClick={() => { | |||
| navigate('/points'); | |||
| }} | |||
| > | |||
| 查看详情 | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default UserPoints; | |||
| @@ -6,6 +6,7 @@ | |||
| background: linear-gradient(#ecf2fe, #f9fafb); | |||
| &__overview { | |||
| flex: 1; | |||
| gap: 15px; | |||
| margin-bottom: 16px; | |||
| padding: 20px 30px; | |||
| @@ -21,11 +22,19 @@ | |||
| &__content { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| gap: 15px; | |||
| align-items: center; | |||
| @media screen and (max-width: 1800px) { | |||
| flex-wrap: wrap; | |||
| &__statistics { | |||
| flex: none; | |||
| background: linear-gradient( | |||
| 123.08deg, | |||
| rgba(138, 138, 138, 0.06) 1.32%, | |||
| rgba(22, 100, 255, 0.02) 58.35% | |||
| ); | |||
| border-radius: 4px; | |||
| } | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ import { useDraggable } from '@/hooks/draggable'; | |||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | |||
| import { ExperimentInstance } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Divider, Flex } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import Draggable from 'react-draggable'; | |||
| import AssetsManagement from './components/AssetsManagement'; | |||
| @@ -10,6 +11,7 @@ import ExperitableTable from './components/ExperimentTable'; | |||
| import QuickStart from './components/QuickStart'; | |||
| import RobotFrame from './components/RobotFrame'; | |||
| import TotalStatistics from './components/TotalStatistics'; | |||
| import UserPoints from './components/UserPoints'; | |||
| import UserSpace from './components/UserSpace'; | |||
| import WorkspaceIntro from './components/WorkspaceIntro'; | |||
| import styles from './index.less'; | |||
| @@ -28,6 +30,7 @@ function Workspace() { | |||
| setRobotFrameVisible((prev) => !prev), | |||
| ); | |||
| const users: number[] = new Array(8).fill(0); | |||
| useEffect(() => { | |||
| getWorkspaceOverview(); | |||
| }, []); | |||
| @@ -43,27 +46,38 @@ function Workspace() { | |||
| return ( | |||
| <div className={styles.workspace}> | |||
| <WorkspaceIntro></WorkspaceIntro> | |||
| <div className={styles['workspace__overview']}> | |||
| <div className={styles['workspace__overview__title']}>运行概览</div> | |||
| <div className={styles['workspace__overview__content']}> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-pipeline.png')} | |||
| title="流水线总数" | |||
| count={overviewData?.workflowCount} | |||
| /> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-experiment.png')} | |||
| title="正在运行实例总数" | |||
| count={overviewData?.runningExperimentInsCount} | |||
| /> | |||
| <ExperitableTable | |||
| tableData={overviewData?.latestExperimentInsList || []} | |||
| ></ExperitableTable> | |||
| {overviewData?.experimentInsStatus && ( | |||
| <ExperimentChart chartData={overviewData?.experimentInsStatus}></ExperimentChart> | |||
| )} | |||
| <Flex gap={'0 15px'} wrap> | |||
| <div className={styles['workspace__overview']}> | |||
| <div className={styles['workspace__overview__title']}>运行概览</div> | |||
| <div className={styles['workspace__overview__content']}> | |||
| <Flex align="center" className={styles['workspace__overview__content__statistics']}> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-pipeline.png')} | |||
| title="流水线总数" | |||
| count={overviewData?.workflowCount} | |||
| /> | |||
| <Divider | |||
| type="vertical" | |||
| dashed | |||
| style={{ color: '1px dashed rgba(96, 107, 122, 0.19)', height: 68 }} | |||
| /> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-experiment.png')} | |||
| title="正在运行实例总数" | |||
| count={overviewData?.runningExperimentInsCount} | |||
| /> | |||
| </Flex> | |||
| <ExperitableTable | |||
| tableData={overviewData?.latestExperimentInsList || []} | |||
| ></ExperitableTable> | |||
| {overviewData?.experimentInsStatus && ( | |||
| <ExperimentChart chartData={overviewData?.experimentInsStatus}></ExperimentChart> | |||
| )} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <UserPoints /> | |||
| </Flex> | |||
| <div className={styles['workspace__quick-start']}> | |||
| <QuickStart></QuickStart> | |||
| <div className={styles['workspace__user']}> | |||
| @@ -0,0 +1,22 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2025-03-20 13:48:53 | |||
| * @Description: 积分 | |||
| */ | |||
| import { request } from '@umijs/max'; | |||
| // 分页查询积分消费明细 | |||
| export function getPointsConsumptionReq(params: any) { | |||
| return request(`/api/mmp/computingResource/resouceOccupy`, { | |||
| method: 'GET', | |||
| params, | |||
| }); | |||
| } | |||
| // 分页查询积分消费明细 | |||
| export function getPointsStatisticsReq() { | |||
| return request(`/api/mmp/computingResource/credit`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| @@ -69,6 +69,14 @@ | |||
| -webkit-line-clamp: @line; | |||
| } | |||
| // 背景 | |||
| .backgroundFullImage(@url) { | |||
| background-image: @url; | |||
| background-repeat: no-repeat; | |||
| background-position: top center; | |||
| background-size: 100% 100%; | |||
| } | |||
| // 导出变量 | |||
| :export { | |||
| primaryColor: @primary-color; | |||
| @@ -21,6 +21,7 @@ declare namespace API.System { | |||
| remark: string; | |||
| gitLinkUsername?: string; | |||
| gitLinkPassword?: string; | |||
| credit?: number; | |||
| } | |||
| export interface UserListParams { | |||
| @@ -0,0 +1,23 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2025-03-20 14:33:01 | |||
| * @Description: 通用的 Table 状态单元格 | |||
| */ | |||
| export type StatusInfo = { | |||
| value: number | string; | |||
| label: string; | |||
| color?: string; | |||
| }; | |||
| function statusTableCell(infos: StatusInfo[]) { | |||
| return function (status?: string | number | null) { | |||
| const info = infos.find((item) => item.value === status); | |||
| if (status === null || status === undefined || !info) { | |||
| return <span>--</span>; | |||
| } | |||
| return <span style={{ color: info.color }}>{info.label}</span>; | |||
| }; | |||
| } | |||
| export default statusTableCell; | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-06-26 10:05:52 | |||
| * @Description: Table cell 自定义 render | |||
| * @Description: Table Cell 自定义 render | |||
| */ | |||
| import { isEmpty } from '@/utils'; | |||