diff --git a/react-ui/package.json b/react-ui/package.json
index 4b28d7cf..a4e0635c 100644
--- a/react-ui/package.json
+++ b/react-ui/package.json
@@ -8,7 +8,7 @@
"build": "max build",
"deploy": "npm run build && npm run gh-pages",
"dev": "npm run start:dev",
- "dev-no-sso": "cross-env NO_SSO=true npm run start:mock",
+ "dev-no-sso": "cross-env NO_SSO=true PORT=8001 npm run start:mock",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
@@ -75,6 +75,7 @@
"classnames": "^2.3.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.0",
+ "echarts-gl": "~2.0.9",
"fabric": "^5.3.0",
"highlight.js": "^11.7.0",
"lodash": "^4.17.21",
diff --git a/react-ui/src/assets/img/logo-ccc.png b/react-ui/src/assets/img/logo-ccc.png
new file mode 100644
index 00000000..e2fbcfe5
Binary files /dev/null and b/react-ui/src/assets/img/logo-ccc.png differ
diff --git a/react-ui/src/assets/img/logo.png b/react-ui/src/assets/img/logo.png
index e2fbcfe5..865ecdf5 100644
Binary files a/react-ui/src/assets/img/logo.png and b/react-ui/src/assets/img/logo.png differ
diff --git a/react-ui/src/assets/img/workspace/app.png b/react-ui/src/assets/img/workspace/app.png
new file mode 100644
index 00000000..af6461f6
Binary files /dev/null and b/react-ui/src/assets/img/workspace/app.png differ
diff --git a/react-ui/src/assets/img/workspace/banner1.png b/react-ui/src/assets/img/workspace/banner1.png
new file mode 100644
index 00000000..21f23634
Binary files /dev/null and b/react-ui/src/assets/img/workspace/banner1.png differ
diff --git a/react-ui/src/assets/img/workspace/banner2.png b/react-ui/src/assets/img/workspace/banner2.png
new file mode 100644
index 00000000..a9f1e148
Binary files /dev/null and b/react-ui/src/assets/img/workspace/banner2.png differ
diff --git a/react-ui/src/assets/img/workspace/block-title-bg.png b/react-ui/src/assets/img/workspace/block-title-bg.png
new file mode 100644
index 00000000..dbdbf1f8
Binary files /dev/null and b/react-ui/src/assets/img/workspace/block-title-bg.png differ
diff --git a/react-ui/src/assets/img/workspace/code.png b/react-ui/src/assets/img/workspace/code.png
new file mode 100644
index 00000000..60b20ba6
Binary files /dev/null and b/react-ui/src/assets/img/workspace/code.png differ
diff --git a/react-ui/src/assets/img/workspace/dataset.png b/react-ui/src/assets/img/workspace/dataset.png
new file mode 100644
index 00000000..ac5f0d6e
Binary files /dev/null and b/react-ui/src/assets/img/workspace/dataset.png differ
diff --git a/react-ui/src/assets/img/workspace/echart-bg.png b/react-ui/src/assets/img/workspace/echart-bg.png
new file mode 100644
index 00000000..55a53973
Binary files /dev/null and b/react-ui/src/assets/img/workspace/echart-bg.png differ
diff --git a/react-ui/src/assets/img/workspace/experiment.png b/react-ui/src/assets/img/workspace/experiment.png
new file mode 100644
index 00000000..3e5ae383
Binary files /dev/null and b/react-ui/src/assets/img/workspace/experiment.png differ
diff --git a/react-ui/src/assets/img/workspace/hot-icon.png b/react-ui/src/assets/img/workspace/hot-icon.png
new file mode 100644
index 00000000..dbad86bc
Binary files /dev/null and b/react-ui/src/assets/img/workspace/hot-icon.png differ
diff --git a/react-ui/src/assets/img/workspace/image.png b/react-ui/src/assets/img/workspace/image.png
new file mode 100644
index 00000000..82f1c29f
Binary files /dev/null and b/react-ui/src/assets/img/workspace/image.png differ
diff --git a/react-ui/src/assets/img/workspace/model.png b/react-ui/src/assets/img/workspace/model.png
new file mode 100644
index 00000000..b4fdf7b9
Binary files /dev/null and b/react-ui/src/assets/img/workspace/model.png differ
diff --git a/react-ui/src/assets/img/workspace/notebook-icon.png b/react-ui/src/assets/img/workspace/notebook-icon.png
new file mode 100644
index 00000000..ebc03e95
Binary files /dev/null and b/react-ui/src/assets/img/workspace/notebook-icon.png differ
diff --git a/react-ui/src/assets/img/workspace/paper.png b/react-ui/src/assets/img/workspace/paper.png
new file mode 100644
index 00000000..9b688f02
Binary files /dev/null and b/react-ui/src/assets/img/workspace/paper.png differ
diff --git a/react-ui/src/assets/img/workspace/points-bg.png b/react-ui/src/assets/img/workspace/points-bg.png
new file mode 100644
index 00000000..0778dce8
Binary files /dev/null and b/react-ui/src/assets/img/workspace/points-bg.png differ
diff --git a/react-ui/src/assets/img/workspace/prompt-pilot.png b/react-ui/src/assets/img/workspace/prompt-pilot.png
new file mode 100644
index 00000000..865ecdf5
Binary files /dev/null and b/react-ui/src/assets/img/workspace/prompt-pilot.png differ
diff --git a/react-ui/src/assets/img/workspace/search-icon.png b/react-ui/src/assets/img/workspace/search-icon.png
new file mode 100644
index 00000000..76b1dba5
Binary files /dev/null and b/react-ui/src/assets/img/workspace/search-icon.png differ
diff --git a/react-ui/src/assets/img/workspace/search-star.gif b/react-ui/src/assets/img/workspace/search-star.gif
new file mode 100644
index 00000000..d60d43f4
Binary files /dev/null and b/react-ui/src/assets/img/workspace/search-star.gif differ
diff --git a/react-ui/src/assets/img/workspace/statistics-bg.png b/react-ui/src/assets/img/workspace/statistics-bg.png
new file mode 100644
index 00000000..c3ac4a37
Binary files /dev/null and b/react-ui/src/assets/img/workspace/statistics-bg.png differ
diff --git a/react-ui/src/assets/img/workspace/title-icon.png b/react-ui/src/assets/img/workspace/title-icon.png
new file mode 100644
index 00000000..47754168
Binary files /dev/null and b/react-ui/src/assets/img/workspace/title-icon.png differ
diff --git a/react-ui/src/assets/img/workspace/train.png b/react-ui/src/assets/img/workspace/train.png
new file mode 100644
index 00000000..98c1a175
Binary files /dev/null and b/react-ui/src/assets/img/workspace/train.png differ
diff --git a/react-ui/src/assets/img/workspace/workspace-bg.png b/react-ui/src/assets/img/workspace/workspace-bg.png
new file mode 100644
index 00000000..e52a37f3
Binary files /dev/null and b/react-ui/src/assets/img/workspace/workspace-bg.png differ
diff --git a/react-ui/src/hooks/useRootFontSize.ts b/react-ui/src/hooks/useRootFontSize.ts
new file mode 100644
index 00000000..525dc889
--- /dev/null
+++ b/react-ui/src/hooks/useRootFontSize.ts
@@ -0,0 +1,29 @@
+import { useEffect, useState } from 'react';
+
+/**
+ * 获取 root font size
+ *
+ * @param initialValue - 状态的初始值
+ * @return 包含状态值、状态设置函数和可变引用对象的数组
+ */
+export function useRootFontSize() {
+ const fontSizeStr = document.documentElement.style.fontSize;
+ const fontSize = parseFloat(fontSizeStr);
+ const [size, setSize] = useState(fontSize);
+
+ useEffect(() => {
+ const setFontSize = () => {
+ setTimeout(() => {
+ const fontSizeStr = document.documentElement.style.fontSize;
+ const fontSize = parseFloat(fontSizeStr);
+ setSize(fontSize);
+ }, 300);
+ };
+ window.addEventListener('resize', setFontSize);
+ return () => {
+ window.removeEventListener('resize', setFontSize);
+ };
+ }, []);
+
+ return size;
+}
diff --git a/react-ui/src/iconfont/iconfont.js b/react-ui/src/iconfont/iconfont.js
index 38c8ec18..098ec118 100644
--- a/react-ui/src/iconfont/iconfont.js
+++ b/react-ui/src/iconfont/iconfont.js
@@ -1 +1 @@
-window._iconfont_svg_string_4511447='',(t=>{var a=(h=(h=document.getElementsByTagName("script"))[h.length-1]).getAttribute("data-injectcss"),h=h.getAttribute("data-disable-injectsvg");if(!h){var l,v,z,i,o,p=function(a,h){h.parentNode.insertBefore(a,h)};if(a&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}l=function(){var a,h=document.createElement("div");h.innerHTML=t._iconfont_svg_string_4511447,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?p(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),l()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(z=l,i=t.document,o=!1,d(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,m())})}function m(){o||(o=!0,z())}function d(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(d,50)}m()}})(window);
\ No newline at end of file
+window._iconfont_svg_string_4511447='',(l=>{var a=(h=(h=document.getElementsByTagName("script"))[h.length-1]).getAttribute("data-injectcss"),h=h.getAttribute("data-disable-injectsvg");if(!h){var t,v,z,i,o,p=function(a,h){h.parentNode.insertBefore(a,h)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}t=function(){var a,h=document.createElement("div");h.innerHTML=l._iconfont_svg_string_4511447,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?p(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),t()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(z=t,i=l.document,o=!1,d(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,m())})}function m(){o||(o=!0,z())}function d(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(d,50)}m()}})(window);
\ No newline at end of file
diff --git a/react-ui/src/pages/Workspace/components/AssetsManagement/index.less b/react-ui/src/pages/Workspace/components/AssetsManagement/index.less
index 41ba9f93..fde5a36f 100644
--- a/react-ui/src/pages/Workspace/components/AssetsManagement/index.less
+++ b/react-ui/src/pages/Workspace/components/AssetsManagement/index.less
@@ -1,65 +1,56 @@
.assets-management {
- flex: 1;
+ display: flex;
+ flex-direction: column;
width: 100%;
- padding: 20px 20px 0;
- background-color: white;
- border-radius: 4px;
+ height: 10.875rem;
+ color: white;
+ .backgroundFullImage(url(@/assets/img/workspace/statistics-bg.png));
- :global {
- .ant-select-filled {
- background-color: rgba(138, 138, 138, 0.12);
- border-radius: 2px;
-
- .ant-select-selection-item {
- color: @text-color-secondary !important;
- font-size: 13px;
- }
- }
+ &__title {
+ flex: none;
+ padding: 1.25rem;
+ font-weight: 700;
+ font-size: 1rem;
}
- &__title {
- color: @text-color;
- font-weight: 500;
- font-size: @font-size-title;
+ &__line {
+ flex: none;
+ width: 64.9375rem;
+ height: 1px;
+ padding-left: 1.25rem;
+ background-image: linear-gradient(
+ 135.57deg,
+ rgba(255, 255, 255, 0.3) 0%,
+ rgba(255, 255, 255, 0) 100%
+ );
}
- &__increase {
- display: inline-block;
- margin-top: 12px;
- margin-bottom: 30px;
- padding: 2px 7px;
- color: @primary-color;
- font-size: 13px;
- background-color: rgba(187, 210, 255, 0.29);
- border-radius: 2px;
+ &__content {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ padding: 0 1.25rem;
}
&__summary {
display: flex;
flex-direction: column;
- width: 40%;
- text-align: left;
+ align-items: center;
+ width: 8.125rem;
+ border-right: 1px solid rgba(255, 255, 255, 0.2);
- &:nth-child(3n + 2) {
- width: 30%;
- text-align: center;
+ &:last-child {
+ border-right: none;
}
- &:nth-child(3n) {
- width: 30%;
- text-align: right;
+ &__value {
+ margin-bottom: 4px;
+ font-weight: 700;
+ font-size: 1.25rem;
}
&__title {
- margin-bottom: 12px;
- color: @text-color-secondary;
- font-size: @font-size;
- }
-
- &__value {
- color: @text-color;
- font-weight: 500;
- font-size: 22px;
+ font-size: 0.875rem;
}
}
}
diff --git a/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx b/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
index 1e5b2989..e5941f12 100644
--- a/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
+++ b/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
@@ -1,12 +1,15 @@
import { CommonTabKeys } from '@/enums';
import { getWorkspaceAssetCountReq } from '@/services/workspace';
import { to } from '@/utils/promise';
-import { Flex, Select } from 'antd';
+import { Flex, Typography } from 'antd';
import { useEffect, useState } from 'react';
import styles from './index.less';
+
function AssetsManagement() {
const [type, setType] = useState(CommonTabKeys.Public);
- const [assetCounts, setAssetCounts] = useState<{ title: string; value: number }[]>([]);
+ const [assetCounts, setAssetCounts] = useState<{ title: string; value: number; icon: string }[]>(
+ [],
+ );
useEffect(() => {
// 获取工作空间资产数量
@@ -21,34 +24,37 @@ function AssetsManagement() {
{
title: '数据集',
value: dataset,
+ icon: 'dataset.png',
},
{
title: '模型',
value: model,
+ icon: 'model.png',
+ },
+ {
+ title: '代码配置',
+ value: 0,
+ icon: 'code.png',
},
{
title: '镜像',
value: image,
+ icon: 'image.png',
},
- // {
- // title: '组件',
- // value: component,
- // },
- // {
- // title: '代码配置',
- // value: 0,
- // },
{
- title: '流水线模版',
- value: workflow,
+ title: 'APP',
+ value: service,
+ icon: 'app.png',
},
{
- title: '代码配置',
- value: codeConfig,
+ title: '流水线模板',
+ value: workflow,
+ icon: 'experiment.png',
},
{
- title: '服务',
- value: service,
+ title: 'NOTEBOOKS',
+ value: codeConfig,
+ icon: 'train.png',
},
];
setAssetCounts(items);
@@ -60,26 +66,18 @@ function AssetsManagement() {
return (
-
- 多形态资源库
-
-
- {/*
今日新增数量:5
*/}
-
+ 个人资源库统计
+
+
{assetCounts.map((item, index) => (
-
+
+
+ {item.value ?? '-'}
+
{item.title}
-
{item.value ?? '-'}
))}
diff --git a/react-ui/src/pages/Workspace/components/BlockTitle/index.less b/react-ui/src/pages/Workspace/components/BlockTitle/index.less
new file mode 100644
index 00000000..ab9aaad7
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/BlockTitle/index.less
@@ -0,0 +1,27 @@
+.block-title {
+ display: flex;
+ align-items: center;
+ padding: 0.9375rem 1.25rem;
+ color: @text-color;
+ font-weight: 700;
+ font-size: 1rem;
+ .backgroundFullImage(url(@/assets/img/workspace/block-title-bg.png));
+
+ &__icon {
+ width: 1.875rem;
+ height: 1.875rem;
+ margin-right: 0.9375rem;
+ }
+
+ &__right {
+ margin-right: 0;
+ margin-left: auto;
+ color: @text-color-secondary;
+ font-weight: normal;
+ font-size: 0.875rem;
+
+ &:hover {
+ color: @primary-color;
+ }
+ }
+}
diff --git a/react-ui/src/pages/Workspace/components/BlockTitle/index.tsx b/react-ui/src/pages/Workspace/components/BlockTitle/index.tsx
new file mode 100644
index 00000000..5cb2bd67
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/BlockTitle/index.tsx
@@ -0,0 +1,28 @@
+import KFIcon from '@/components/KFIcon';
+import { Flex } from 'antd';
+import styles from './index.less';
+
+export type BlockTitleProps = {
+ title: string;
+ viewMore?: () => void;
+};
+
+function BlockTitle({ title, viewMore }: BlockTitleProps) {
+ return (
+
+
})
+
{title}
+ {viewMore && (
+
+ 查看全部
+
+
+ )}
+
+ );
+}
+
+export default BlockTitle;
diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/charts.js b/react-ui/src/pages/Workspace/components/ExperimentChart/charts.js
new file mode 100644
index 00000000..09337704
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/ExperimentChart/charts.js
@@ -0,0 +1,285 @@
+/**
+ * 绘制3d图
+ * @param pieData 总数据
+ * @param internalDiameterRatio:透明的空心占比
+ * @param distance 视角到主体的距离
+ * @param alpha 旋转角度
+ * @param pieHeight 立体的高度
+ * @param opacity 饼或者环的透明度
+ */
+
+const setSeriesItem = (item, k) => {
+ const seriesItem = {
+ name:
+ typeof item.name === 'undefined'
+ ? `series${item}`
+ : item.name,
+ type: 'surface',
+ parametric: true,
+ wireframe: {
+ show: false
+ },
+ pieData: item,
+ pieStatus: {
+ selected: false,
+ hovered: false,
+ k: k
+ },
+ center: ['10%', '50%']
+ }
+ if ( item.color) {
+ seriesItem.itemStyle = { color: item.color }
+ }
+
+ return seriesItem
+}
+
+export const getPie3D = (pieData, internalDiameterRatio, distance, alpha, pieHeight, opacity = 1) => {
+ const series = []
+ let sumValue = 0
+ let startValue = 0
+ let endValue = 0
+ let legendData = []
+ let legendBfb = []
+ const k = 1 - internalDiameterRatio
+
+ pieData.sort((a, b) => {
+ return b.value - a.value
+ })
+ // 为每一个饼图数据,生成一个 series-surface 配置
+ for (let i = 0; i < pieData.length; i++) {
+ sumValue += pieData[i].value
+ const seriesItem = setSeriesItem(pieData[i], k)
+ // if (typeof pieData[i].itemStyle !== 'undefined') {
+ // const itemStyle = {}
+ // itemStyle.color =
+ // typeof pieData[i].itemStyle.color !== 'undefined'
+ // ? pieData[i].itemStyle.color
+ // : opacity
+ // itemStyle.opacity =
+ // typeof pieData[i].itemStyle.opacity !== 'undefined'
+ // ? pieData[i].itemStyle.opacity
+ // : opacity
+ // seriesItem.itemStyle = itemStyle
+ // }
+ series.push(seriesItem);
+ }
+
+ if (sumValue === 0) {
+ series.length = 0
+ series.push(setSeriesItem({ value: 100, color: '#d3d3d3', name: '暂无数据' }))
+ }
+
+
+ // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
+ // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
+ legendData = []
+ legendBfb = []
+ for (let i = 0; i < series.length; i++) {
+ endValue = startValue + series[i].pieData.value
+ series[i].pieData.startRatio = startValue / sumValue
+ series[i].pieData.endRatio = endValue / sumValue
+ series[i].parametricEquation = getParametricEquation(
+ series[i].pieData.startRatio,
+ series[i].pieData.endRatio,
+ false,
+ false,
+ k,
+ series[i].pieData.value
+ )
+ startValue = endValue
+ const bfb = fomatFloat(series[i].pieData.value / sumValue, 4)
+ legendData.push({
+ name: series[i].name,
+ value: bfb
+ })
+ legendBfb.push({
+ name: series[i].name,
+ value: bfb
+ })
+ }
+ const boxHeight = getHeight3D(series, pieHeight) // 通过pieHeight设定3d饼/环的高度,单位是px
+ // 准备待返回的配置项,把准备好的 legendData、series 传入。
+ const option = {
+ legend: {
+ show: false,
+ data: legendData,
+ orient: 'vertical',
+ left: 10,
+ top: 10,
+ itemGap: 10,
+ textStyle: {
+ color: '#A1E2FF'
+ },
+ icon: 'rect',
+ formatter: function (param) {
+ const item = legendBfb.filter(item => item.name === param)[0]
+ const bfs = fomatFloat(item.value * 100, 2) + '%'
+ return `${item.name}`
+ }
+ },
+ tooltip: {
+ backgroundColor: '#033b77',
+ borderColor: '#21f2c4',
+ textStyle: {
+ color: '#fff',
+ fontSize: 13
+ },
+ formatter: params => {
+ if (
+ params.seriesName !== 'mouseoutSeries' &&
+ params.seriesName !== 'pie2d'
+ ) {
+ const bfb = (
+ (option.series[params.seriesIndex].pieData.endRatio -
+ option.series[params.seriesIndex].pieData.startRatio) *
+ 100
+ ).toFixed(2)
+ if (bfb === 'NaN') {
+ return (
+ `${params.seriesName}`
+ )
+ } else {
+ return (
+ `${params.seriesName}
` +
+ `
` +
+ `${bfb}%`
+ )
+ }
+ }
+ }
+ },
+ xAxis3D: {
+ min: -1,
+ max: 1
+ },
+ yAxis3D: {
+ min: -1,
+ max: 1
+ },
+ zAxis3D: {
+ min: -1,
+ max: 1
+ },
+ grid3D: {
+ top: -20,
+ show: false,
+ boxHeight: 8, // 圆环的高度
+ viewControl: {
+ // 3d效果可以放大、旋转等,请自己去查看官方配置
+ alpha, // 角度
+ distance, // 调整视角到主体的距离,类似调整zoom
+ rotateSensitivity: 0, // 设置为0无法旋转
+ zoomSensitivity: 0, // 设置为0无法缩放
+ panSensitivity: 0, // 设置为0无法平移
+ autoRotate: false // 自动旋转
+ }
+ },
+ series:series
+ }
+ return option
+}
+
+/**
+ * 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
+ */
+export const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h) => {
+ // 计算
+ const midRatio = (startRatio + endRatio) / 2
+ const startRadian = startRatio * Math.PI * 2
+ const endRadian = endRatio * Math.PI * 2
+ const midRadian = midRatio * Math.PI * 2
+ // 如果只有一个扇形,则不实现选中效果。
+ if (startRatio === 0 && endRatio === 1) {
+ isSelected = false
+ }
+ // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
+ k = typeof k !== 'undefined' ? k : 1 / 3
+ // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
+ const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
+ const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
+ // 计算高亮效果的放大比例(未高亮,则比例为 1)
+ const hoverRate = isHovered ? 1.05 : 1
+ // 返回曲面参数方程
+ return {
+ u: {
+ min: -Math.PI,
+ max: Math.PI * 3,
+ step: Math.PI / 32
+ },
+ v: {
+ min: 0,
+ max: Math.PI * 2,
+ step: Math.PI / 20
+ },
+ x: function (u, v) {
+ if (u < startRadian) {
+ return (
+ offsetX +
+ Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
+ )
+ }
+ if (u > endRadian) {
+ return (
+ offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
+ )
+ }
+ return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
+ },
+ y: function (u, v) {
+ if (u < startRadian) {
+ return (
+ offsetY +
+ Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
+ )
+ }
+ if (u > endRadian) {
+ return (
+ offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
+ )
+ }
+ return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
+ },
+ z: function (u, v) {
+ if (u < -Math.PI * 0.5) {
+ return Math.sin(u)
+ }
+ if (u > Math.PI * 2.5) {
+ return Math.sin(u) * 1
+ }
+ return Math.sin(v) > 0 ? 1 : -1
+ }
+ }
+}
+
+/**
+ * 获取3d丙图的最高扇区的高度
+ */
+const getHeight3D = (series, height) => {
+ series.sort((a, b) => {
+ return b.pieData.value - a.pieData.value
+ })
+ return (height * 25) / series[0].pieData.value
+}
+
+/**
+ * 格式化浮点数
+ */
+const fomatFloat = (num, n) => {
+ let f = parseFloat(num)
+ if (isNaN(f)) {
+ return false
+ }
+ f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n) // n 幂
+ let s = f.toString()
+ let rs = s.indexOf('.')
+ // 判定如果是整数,增加小数点再补0
+ if (rs < 0) {
+ rs = s.length
+ s += '.'
+ }
+ while (s.length <= rs + n) {
+ s += '0'
+ }
+ return s
+}
diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/index.less b/react-ui/src/pages/Workspace/components/ExperimentChart/index.less
index 3af052da..4643bfb6 100644
--- a/react-ui/src/pages/Workspace/components/ExperimentChart/index.less
+++ b/react-ui/src/pages/Workspace/components/ExperimentChart/index.less
@@ -1,8 +1,14 @@
.experiment-chart {
- flex: none;
- width: 295px;
- min-width: 295px;
- height: 140px;
- background: @workspace-background;
- border-radius: 4px;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 15rem;
+ margin-top: 1.25rem;
+
+ &__title {
+ margin-bottom: 1.25rem;
+ color: @text-color;
+ font-weight: 700;
+ font-size: 1rem;
+ }
}
diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx b/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
index 235683ee..2fa0c22b 100644
--- a/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
+++ b/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
@@ -1,3 +1,4 @@
+import { useRootFontSize } from '@/hooks/useRootFontSize';
import themes from '@/styles/theme.less';
import * as echarts from 'echarts';
import React, { useEffect, useMemo, useRef } from 'react';
@@ -87,6 +88,8 @@ type ExperimentChartProps = {
};
function ExperimentChart({ chartData, style }: ExperimentChartProps) {
+ const fontSize = useRootFontSize();
+
const chartRef = useRef
(null);
const total =
chartData.Failed +
@@ -99,21 +102,21 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
() => ({
title: {
show: true,
- left: '29%',
- top: 'center',
+ left: '50%',
+ top: 3.5 * fontSize,
textAlign: 'center',
text: [`{a|${total}}`, '{b|实验状态}'].join('\n'),
textStyle: {
rich: {
a: {
color: themes['textColor'],
- fontSize: 20,
+ fontSize: 1.25 * fontSize,
fontWeight: 700,
- lineHeight: 28,
+ lineHeight: 1.75 * fontSize,
},
b: {
color: themes['textColorSecondary'],
- fontSize: 10,
+ fontSize: 0.625 * fontSize,
fontWeight: 'normal',
},
},
@@ -123,20 +126,22 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
trigger: 'item',
},
legend: {
- top: 'center',
- right: '5%',
- orient: 'vertical',
+ bottom: '0',
+ left: 'center',
+ orient: 'horizontal',
icon: 'circle',
- itemWidth: 6,
- itemGap: 20,
- height: 100,
+ itemWidth: 0.75 * fontSize,
+ itemGap: 1.25 * fontSize,
+ textStyle: {
+ fontSize: 0.75 * fontSize,
+ },
},
color: [color1, color2, color3, color4, color5],
series: [
{
type: 'pie',
radius: ['70%', '80%'],
- center: ['30%', '50%'],
+ center: ['50%', '40%'],
avoidLabelOverlap: false,
padAngle: 3,
itemStyle: {
@@ -165,7 +170,7 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
{
type: 'pie',
radius: '60%',
- center: ['30%', '50%'],
+ center: ['50%', '40%'],
avoidLabelOverlap: false,
label: {
show: false,
@@ -192,7 +197,7 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
},
],
}),
- [chartData, total],
+ [chartData, total, fontSize],
);
useEffect(() => {
@@ -202,16 +207,24 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
// 设置图表实例的配置项和数据
chart.setOption(options);
+ // 改变大小
+ const chartResize = () => {
+ chart.resize();
+ };
+
+ window.addEventListener('resize', chartResize);
+
// 组件卸载
return () => {
- // myChart.dispose() 销毁实例
+ window.removeEventListener('resize', chartResize);
chart.dispose();
};
}, [options]);
return (
);
}
diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/test.tsx b/react-ui/src/pages/Workspace/components/ExperimentChart/test.tsx
new file mode 100644
index 00000000..6c559729
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/ExperimentChart/test.tsx
@@ -0,0 +1,125 @@
+import * as echarts from 'echarts';
+import React from 'react';
+import styles from './index.less';
+import ThreePieChart from './threePieChart';
+
+const color1 = new echarts.graphic.LinearGradient(
+ 0,
+ 0,
+ 0,
+ 1,
+ [
+ { offset: 0, color: '#c73131' },
+ { offset: 1, color: '#ff7e96' },
+ ],
+ false,
+);
+
+const color2 = new echarts.graphic.LinearGradient(
+ 0,
+ 0,
+ 0,
+ 1,
+ [
+ { offset: 0, color: '#6ac21d' },
+ { offset: 1, color: '#96e850' },
+ ],
+ false,
+);
+const color3 = new echarts.graphic.LinearGradient(
+ 0,
+ 0,
+ 0,
+ 1,
+ [
+ { offset: 0, color: '#8c8c8c' },
+ { offset: 1, color: '#c8c6c6' },
+ ],
+ false,
+);
+const color4 = new echarts.graphic.LinearGradient(
+ 0,
+ 0,
+ 0,
+ 1,
+ [
+ { offset: 0, color: '#ecb934' },
+ { offset: 1, color: '#f0864d' },
+ ],
+ false,
+);
+
+const color5 = new echarts.graphic.LinearGradient(
+ 0,
+ 0,
+ 0,
+ 1,
+ [
+ { offset: 0, color: '#7ea9fe' },
+ { offset: 1, color: '#1664ff' },
+ ],
+ false,
+);
+
+const color6 = new echarts.graphic.LinearGradient(
+ 0,
+ 0,
+ 0,
+ 1,
+ [
+ { offset: 0, color: 'rgba(255, 255, 255, 0.62)' },
+ { offset: 1, color: '#ebf2ff ' },
+ ],
+ false,
+);
+
+export type ExperimentStatistics = {
+ Failed: number;
+ Pending: number;
+ Running: number;
+ Succeeded: number;
+ Terminated: number;
+};
+
+type ExperimentChartProps = {
+ style?: React.CSSProperties;
+ chartData: ExperimentStatistics;
+};
+
+function ExperimentChart({ chartData, style }: ExperimentChartProps) {
+ // useEffect(() => {
+ // // 创建一个echarts实例,返回echarts实例
+ // const chart = echarts.init(chartRef.current);
+
+ // // 设置图表实例的配置项和数据
+ // chart.setOption(options);
+
+ // // 组件卸载
+ // return () => {
+ // // myChart.dispose() 销毁实例
+ // chart.dispose();
+ // };
+ // }, [options]);
+
+ const pieData = [
+ { name: '销售业务', value: 77.56, color: '#f00' },
+ { name: '技术服务', value: 18.21, color: '#0f0' },
+ { name: '配送业务', value: 3.67, color: '#00f' },
+ { name: '其他业务', value: 0.17, color: '#ff0' },
+ ];
+
+ return (
+
+ );
+}
+
+export default ExperimentChart;
diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/threePieChart.jsx b/react-ui/src/pages/Workspace/components/ExperimentChart/threePieChart.jsx
new file mode 100644
index 00000000..734f6477
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/ExperimentChart/threePieChart.jsx
@@ -0,0 +1,84 @@
+import * as echarts from 'echarts';
+import { useEffect } from 'react';
+import { getPie3D } from './charts';
+function PieEchart({ id, data, style }) {
+ useEffect(() => {
+ if (data && data.length > 0) {
+ init(setLabel(data));
+ }
+ }, [data]);
+
+ const init = (data) => {
+ let huan_val = document.getElementById(id);
+ let myEcharts = echarts.init(huan_val);
+
+ let options = getOption(data);
+ console.log(options);
+ options.color = ['#4154f1', '#ffa900', '#00aa82', '#a296fd', '#40acfc'];
+ options.legend = {
+ orient: 'horizontal',
+ right: 'center',
+ bottom: 40,
+ width: '100%',
+ itemWidth: 11,
+ itemHeight: 11,
+ };
+ options.grid3D.top = -50;
+ options.series.map((item) => {
+ item.radius = [30, 120];
+ item.height = 20;
+ });
+ console.log(options);
+ myEcharts.setOption(options);
+ };
+ const getOption = (data) => {
+ console.log(data);
+ let option = getPie3D(data, 0.86, 150, 20, 20, 1);
+ return option;
+ };
+ const setLabel = (data) => {
+ console.log(data, 'data');
+
+ data.forEach((item, index) => {
+ item.itemStyle = {
+ color: item.color,
+ };
+ item.label = {
+ normal: {
+ show: true,
+ color: item.color,
+ rich: {
+ b: {
+ color: '#fff',
+ lineHeight: 25,
+ align: 'left',
+ },
+ c: {
+ fontSize: 22,
+ color: '#fff',
+ textShadowColor: '#1c90a6',
+ textShadowOffsetX: 0,
+ textShadowOffsetY: 2,
+ textShadowBlur: 5,
+ },
+ d: {
+ color: item.color,
+ align: 'left',
+ },
+ },
+ },
+ };
+ item.labelLine = {
+ normal: {
+ lineStyle: {
+ width: 1,
+ color: 'rgba(255,255,255,0.7)',
+ },
+ },
+ };
+ });
+ return data;
+ };
+ return ;
+}
+export default PieEchart;
diff --git a/react-ui/src/pages/Workspace/components/Experiments/index.less b/react-ui/src/pages/Workspace/components/Experiments/index.less
new file mode 100644
index 00000000..8528c0ed
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Experiments/index.less
@@ -0,0 +1,61 @@
+.experiments {
+ width: calc(40% - 1.25rem);
+ margin-left: 1.25rem;
+ background-color: white;
+ border-radius: 10px;
+ box-shadow: 0rem 0.1875rem 0.75rem rgba(151, 150, 180, 0.08);
+
+ &__content {
+ width: 100%;
+ padding: 1.25rem;
+ }
+
+ &__item {
+ margin-bottom: 0.875rem;
+ padding: 1rem 1.25rem;
+ background-color: #f7f9fd;
+ border: 1px solid transparent;
+ border-radius: 0.5rem;
+ cursor: pointer;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &:hover {
+ background-image: linear-gradient(173.59deg, #e7e8ff 0%, #f7f9fd 20.63%, #f7f9fd 100%);
+ border: 1px solid @primary-color;
+ }
+
+ &__title {
+ flex: 0 1 fit-content;
+ color: @text-color;
+ font-size: 1rem;
+ }
+
+ &__status {
+ margin-left: 1rem;
+ padding: 0.125rem 0.25rem;
+ color: #1664ff;
+ font-size: 0.75rem;
+ background-color: rgba(22, 100, 255, 0.07);
+ border-radius: 4px;
+ }
+
+ &__time {
+ margin-right: 0;
+ margin-left: auto;
+ color: @text-color-secondary;
+ font-size: 0.8125rem;
+ }
+
+ &:hover &__title {
+ color: @primary-color;
+ }
+
+ &__desc {
+ margin-top: 0.875rem;
+ font-size: 0.875rem;
+ }
+ }
+}
diff --git a/react-ui/src/pages/Workspace/components/Experiments/index.tsx b/react-ui/src/pages/Workspace/components/Experiments/index.tsx
new file mode 100644
index 00000000..d5be9dd2
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Experiments/index.tsx
@@ -0,0 +1,82 @@
+import { ExperimentInstance } from '@/types';
+import { formatDate } from '@/utils/date';
+import { useNavigate } from '@umijs/max';
+import { Flex, Typography } from 'antd';
+import BlockTitle from '../BlockTitle';
+import styles from './index.less';
+
+type ExperimentsProps = {
+ tableData: ExperimentInstance[];
+};
+
+const testData = [
+ {
+ name: '基于pytorch的XXXX开发',
+ description: '基于pytorch的XXXX开发基于pytorch的XXXX开发',
+ status: 'Running',
+ date: new Date(),
+ },
+ {
+ name: '基于pytorch的XXXX开发',
+ description: '基于pytorch的XXXX开发基于pytorch的XXXX开发',
+ status: 'Running',
+ date: new Date(),
+ },
+ {
+ name: '基于pytorch的XXXX开发',
+ description: '基于pytorch的XXXX开发基于pytorch的XXXX开发',
+ status: 'Running',
+ date: new Date(),
+ },
+ {
+ name: '基于pytorch的XXXX开发',
+ description: '基于pytorch的XXXX开发基于pytorch的XXXX开发',
+ status: 'Running',
+ date: new Date(),
+ },
+];
+
+function Experiments({ tableData }: ExperimentsProps) {
+ const navigate = useNavigate();
+ const gotoExperiment = (record: ExperimentInstance) => {
+ navigate(`/pipeline/experiment/instance/${record.workflow_id}/${record.id}`);
+ };
+
+ return (
+
+
{}}>
+
+ {testData.map((item) => {
+ return (
+
gotoExperiment(item)}
+ >
+
+
+ {item.name}
+
+ 运行中
+
+ {formatDate(item.date, 'YYYY-MM-DD')}
+
+
+
+ {item.description}
+
+
+ );
+ })}
+
+
+ );
+}
+
+export default Experiments;
diff --git a/react-ui/src/pages/Workspace/components/Messages/index.less b/react-ui/src/pages/Workspace/components/Messages/index.less
new file mode 100644
index 00000000..690cae0e
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Messages/index.less
@@ -0,0 +1,62 @@
+.messages {
+ flex: 1;
+ margin-top: 1.25rem;
+
+ &__title {
+ color: @text-color;
+ font-weight: 700;
+ font-size: 1rem;
+ }
+
+ &__view-more {
+ margin-right: 0;
+ margin-left: auto;
+ color: @text-color-secondary;
+ font-weight: normal;
+ font-size: 0.875rem;
+
+ &:hover {
+ color: @primary-color;
+ }
+ }
+
+ &__content {
+ width: 100%;
+ margin-top: 1.25rem;
+ padding: 1rem 1.25rem;
+ overflow-y: auto;
+ background-color: #ffffff;
+ border-radius: 10px;
+ box-shadow: 0px 3px 6px rgba(151, 150, 180, 0.08);
+ }
+
+ &__item {
+ display: flex;
+ align-self: center;
+ padding: 0.625rem 0;
+ border-bottom: 1px dashed rgba(130, 132, 164, 0.18);
+
+ &__title {
+ flex: 1;
+ margin-right: 0.625rem;
+ color: @text-color-secondary;
+ font-size: 0.875rem;
+
+ &:hover {
+ color: @primary-color;
+ }
+ }
+
+ &__time {
+ flex: none;
+ margin-right: 0;
+ margin-left: auto;
+ color: @text-color-secondary;
+ font-size: 0.8125rem;
+ }
+
+ &:hover &__title {
+ color: @primary-color;
+ }
+ }
+}
diff --git a/react-ui/src/pages/Workspace/components/Messages/index.tsx b/react-ui/src/pages/Workspace/components/Messages/index.tsx
new file mode 100644
index 00000000..a10c6de0
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Messages/index.tsx
@@ -0,0 +1,106 @@
+import KFIcon from '@/components/KFIcon';
+import { MessageStatus, MessageType } from '@/enums';
+import { Message, MessageResponse } from '@/pages/Message';
+import { getMessageListReq, readMessagesReq } from '@/services/message';
+import { ago } from '@/utils/date';
+import { to } from '@/utils/promise';
+import { useModel, useNavigate } from '@umijs/max';
+import { Empty, Flex, Typography } from 'antd';
+import { useCallback, useEffect, useState } from 'react';
+import styles from './index.less';
+
+function Messages() {
+ const { initialState } = useModel('@@initialState');
+ const { currentUser } = initialState || {};
+ const { userId } = currentUser || {};
+ const navigate = useNavigate();
+ const [messages, setMessages] = useState([]);
+
+ const getMessages = useCallback(async () => {
+ if (!userId) return;
+
+ const params: Record = {
+ receiver: userId,
+ status: MessageStatus.All,
+ type: MessageType.System,
+ page: 1,
+ size: 10,
+ };
+ const [res] = await to(getMessageListReq(params));
+ if (res && res.data) {
+ const { records } = res.data as MessageResponse;
+ setMessages(records);
+ }
+ }, [userId]);
+
+ useEffect(() => {
+ getMessages();
+ }, [getMessages]);
+
+ // 标记为已读
+ const readMessages = async (message: Message) => {
+ const params: Record = {
+ notificationIds: message.id,
+ status: MessageStatus.Readed,
+ receiver: message.receiver,
+ type: message.type,
+ };
+ readMessagesReq(params, true);
+ };
+
+ // 点击消息中心
+ const gotoMessageCenter = () => {
+ navigate(`/workspace/message`);
+ };
+
+ // 点击消息
+ const hanldeMessageClick = (message: Message) => {
+ if (message.status === MessageStatus.UnRead) {
+ readMessages(message);
+ }
+
+ if (message.notification_url) {
+ navigate(message.notification_url);
+ }
+ };
+
+ return (
+
+
+ 消息通知
+
+ 消息中心
+
+
+
+
+ {messages.length > 0 ? (
+ messages.map((item) => {
+ return (
+
hanldeMessageClick(item)}
+ >
+
+ {item.content}
+
+
{ago(item.created_at)}
+
+ );
+ })
+ ) : (
+
+ )}
+
+
+ );
+}
+
+export default Messages;
diff --git a/react-ui/src/pages/Workspace/components/Notebooks/index.less b/react-ui/src/pages/Workspace/components/Notebooks/index.less
new file mode 100644
index 00000000..b4c59bc5
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Notebooks/index.less
@@ -0,0 +1,120 @@
+.notebooks {
+ width: 60%;
+ background-color: white;
+ border-radius: 10px;
+ box-shadow: 0rem 0.1875rem 0.75rem rgba(151, 150, 180, 0.08);
+
+ &__content {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1.25rem;
+ width: 100%;
+ padding: 1.25rem;
+ }
+
+ &__item {
+ width: calc((100% - 1.25rem) / 2);
+ padding: 1.25rem;
+ background-image: linear-gradient(173.59deg, #e7e8ff 0%, #f7f9fd 20.63%, #f7f9fd 100%);
+ border: 1px solid transparent;
+ border-radius: 0.5rem;
+ cursor: pointer;
+
+ &:hover {
+ background-image: linear-gradient(203.57deg, #e7e8ff 0%, #f7f9fd 20.63%, #f7f9fd 100%);
+ border: 1px solid @primary-color;
+ outline: 4px solid .addAlpha(@primary-color, 0.1) [];
+ }
+
+ &__title-icon {
+ flex: none;
+ width: 1.75rem;
+ height: 1.75rem;
+ margin-right: 0.75rem;
+ }
+
+ &__title {
+ flex: 0 1 fit-content;
+ color: @text-color;
+ font-size: 1rem;
+ }
+
+ &:hover &__title {
+ color: @primary-color;
+ }
+
+ &__arrow {
+ position: relative;
+ top: 0.125rem;
+ display: none;
+ width: 0.75rem;
+ height: 0.75rem;
+ margin-left: 0.5rem;
+ color: @primary-color;
+ }
+
+ &:hover &__arrow {
+ display: block;
+ }
+
+ &__desc {
+ margin-top: 0.625rem;
+ margin-bottom: 1.25rem;
+ font-size: 0.875rem;
+ }
+
+ &__user {
+ color: @text-color;
+ font-size: 0.875rem;
+ }
+
+ &__user-divider {
+ flex: none;
+ height: 0.625rem;
+ margin-right: 0.75rem;
+ margin-left: 0.75rem;
+ background-color: @home-divider-color;
+ }
+
+ &__timestamp {
+ flex: none;
+ color: @text-color-secondary;
+ font-size: 0.8125rem;
+ }
+
+ &:hover &__user-divider {
+ background-color: #d7d9e1;
+ }
+
+ &__category {
+ padding: 0.25rem 0.625rem;
+ color: #646a73;
+ font-size: 0.875rem;
+ background-color: .addAlpha(@primary-color, 0.07) [];
+ border-radius: 0.25rem;
+ }
+
+ &:hover &__category {
+ color: @primary-color;
+ }
+
+ &__info-btn {
+ display: none;
+ width: 100%;
+ height: 2rem;
+ }
+
+ &:hover &__info-btn {
+ display: block;
+ }
+
+ &__user-container {
+ display: flex;
+ height: 2rem;
+ }
+
+ &:hover &__user-container {
+ display: none;
+ }
+ }
+}
diff --git a/react-ui/src/pages/Workspace/components/Notebooks/index.tsx b/react-ui/src/pages/Workspace/components/Notebooks/index.tsx
new file mode 100644
index 00000000..d3021bf5
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Notebooks/index.tsx
@@ -0,0 +1,95 @@
+import KFIcon from '@/components/KFIcon';
+import { ModelData } from '@/pages/Dataset/config';
+import { getPublicModelsReq } from '@/services/home';
+import { to } from '@/utils/promise';
+import { gotoPageIfLogin } from '@/utils/ui';
+import { Button, Divider, Flex, Typography } from 'antd';
+import { useEffect, useState } from 'react';
+import BlockTitle from '../BlockTitle';
+import styles from './index.less';
+
+function NotebookComponent() {
+ const [modelData, setModelData] = useState([]);
+
+ useEffect(() => {
+ const getPublicModels = async () => {
+ const [res] = await to(getPublicModelsReq());
+ if (res && res.data) {
+ const { content = [] } = res.data;
+ setModelData(content.slice(0, 4));
+ }
+ };
+
+ getPublicModels();
+ }, []);
+
+ return (
+
+
{}}>
+
+ {modelData.map((item) => {
+ return (
+
{
+ gotoPageIfLogin(
+ `/dataset/model/info/${item.id}?name=${item.name}&owner=${item.owner}&identifier=${item.identifier}&is_public=${item.is_public}`,
+ );
+ }}
+ >
+
+
+
+ {item.name}
+
+
+
+
+ {item.description}
+
+
+
+ 电池开发
+ 材料研发
+
+
+
+
+
+
+ {item.create_by}
+
+
+ {item.time_ago}更新
+
+
+ );
+ })}
+
+
+ );
+}
+
+export default NotebookComponent;
diff --git a/react-ui/src/pages/Workspace/components/Search/index.less b/react-ui/src/pages/Workspace/components/Search/index.less
new file mode 100644
index 00000000..9ec0bc1f
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Search/index.less
@@ -0,0 +1,111 @@
+.workspace-search {
+ padding: 0 170px;
+ color: @text-color;
+ font-size: 18px;
+
+ &__icon {
+ width: 36px;
+ height: 36px;
+ margin-right: 12px;
+ padding: 4px 5px;
+ background-color: white;
+ border-radius: 6px;
+ }
+
+ &__star {
+ position: relative;
+ top: -10px;
+ width: 36px;
+ height: 36px;
+ }
+
+ &__textarea {
+ position: relative;
+ margin-top: 15px;
+ margin-bottom: 20px;
+
+ :global {
+ .ant-input-affix-wrapper {
+ border: 2px solid rgba(22, 100, 255, 0.1);
+ border-radius: 20px;
+ box-shadow: 0px 0px 12px rgba(33, 73, 212, 0.04);
+
+ &:hover {
+ border: 2px solid @primary-color;
+ outline: 4px solid .addAlpha(@primary-color, 0.1) [];
+ }
+ }
+
+ .ant-input {
+ padding: 20px;
+ }
+
+ .ant-btn {
+ position: absolute;
+ right: 20px;
+ bottom: 20px;
+ z-index: 100;
+ padding: 6px 13px;
+ font-size: 14px;
+ background: @text-color !important;
+
+ &:hover {
+ outline: 4px solid .addAlpha(@text-color, 0.15) [];
+ }
+
+ &:active {
+ opacity: 0.8;
+ }
+ }
+ }
+ }
+
+ &__question {
+ display: flex;
+ align-items: center;
+ height: 40px;
+ padding: 0 14px;
+ color: @text-color-secondary;
+ font-size: 14px;
+ border: 1px solid .addAlpha(@text-color-secondary, 0.2) [];
+ border-radius: 15px;
+ box-shadow: 0px 0px 12px rgba(33, 73, 212, 0.04);
+
+ &:hover {
+ color: @primary-color;
+ background-color: rgba(81, 76, 249, 0.05);
+ border: 1px solid .addAlpha(@primary-color, 0.24) [];
+ box-shadow: 0px 0px 12px rgba(33, 73, 212, 0.04);
+ }
+
+ &__icon {
+ width: 19px;
+ height: 19px;
+ margin-right: 8px;
+ }
+ }
+
+ &__try {
+ margin-top: 24px;
+ margin-bottom: 34px;
+ color: @text-color-secondary;
+ font-size: 14px;
+
+ :global {
+ .ant-btn {
+ display: flex;
+ align-items: center;
+ height: 28px;
+ margin-left: 10px;
+ padding: 0 15px;
+ color: @text-color-secondary;
+ font-size: 14px;
+ background: white !important;
+
+ &:hover {
+ color: @primary-color !important;
+ }
+ }
+ }
+ }
+}
diff --git a/react-ui/src/pages/Workspace/components/Search/index.tsx b/react-ui/src/pages/Workspace/components/Search/index.tsx
new file mode 100644
index 00000000..fae7ecc0
--- /dev/null
+++ b/react-ui/src/pages/Workspace/components/Search/index.tsx
@@ -0,0 +1,80 @@
+import KFIcon from '@/components/KFIcon';
+import { Button, Flex, Input } from 'antd';
+import styles from './index.less';
+
+const hotQuestions = [
+ {
+ icon: '',
+ title: '高分子材料有哪些?',
+ },
+ {
+ icon: '',
+ title: '高分子材料有哪些?',
+ },
+ {
+ icon: '',
+ title: '高分子材料有哪些?',
+ },
+ {
+ icon: '',
+ title: '高分子材料有哪些?',
+ },
+];
+
+function WorkspaceSearch() {
+ return (
+
+
+
+ 今天你想问一些什么科学的问题
+
+
+
+
+ }
+ color="default"
+ variant="solid"
+ shape="round"
+ >
+ 开始搜索
+
+
+
+ {hotQuestions.map((item, index) => (
+
+
})
+
{item.title}
+
+ ))}
+
+
+ 试一试主题:梅洛 · 庞蒂的“身体现象学”
+ }
+ color="default"
+ variant="solid"
+ shape="round"
+ size="middle"
+ >
+ 换一换
+
+
+
+ );
+}
+
+export default WorkspaceSearch;
diff --git a/react-ui/src/pages/Workspace/components/UserPoints/index.less b/react-ui/src/pages/Workspace/components/UserPoints/index.less
index 7316a5d6..24c49c80 100644
--- a/react-ui/src/pages/Workspace/components/UserPoints/index.less
+++ b/react-ui/src/pages/Workspace/components/UserPoints/index.less
@@ -1,40 +1,34 @@
.user-points {
- display: flex;
- flex: none;
- flex-direction: column;
- align-items: center;
- width: 326px;
- height: 228px;
- padding: 0 20px;
- .backgroundFullImage(url(@/assets/img/user-points-bg.png));
- margin-bottom: 16px;
-
- &__label {
- margin-top: 60px;
+ &__title {
+ margin-bottom: 1.25rem;
color: @text-color;
- font-size: @font-size-title;
- font-family: DingTalk-JinBuTi;
+ font-weight: 700;
+ font-size: 1rem;
}
- &__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();
- }
+ &__content {
+ display: flex;
+ gap: 0 1.25rem;
+ padding: 1.25rem;
+ background-color: white;
+ border-radius: 0.625rem;
+
+ &__title {
+ margin-bottom: 0.625rem;
+ color: @text-color-secondary;
+ font-size: 0.875rem;
+ }
+
+ &__value {
+ color: @text-color;
+ font-weight: 700;
+ font-size: 1.25rem;
+ }
- &__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;
+ &__unit {
+ position: relative;
+ top: -0.25rem;
+ margin-left: 0.25rem;
+ }
}
}
diff --git a/react-ui/src/pages/Workspace/components/UserPoints/index.tsx b/react-ui/src/pages/Workspace/components/UserPoints/index.tsx
index a6eb3ece..1a9520fb 100644
--- a/react-ui/src/pages/Workspace/components/UserPoints/index.tsx
+++ b/react-ui/src/pages/Workspace/components/UserPoints/index.tsx
@@ -3,7 +3,7 @@ import { getPointsStatisticsReq } from '@/services/points';
import { formatNumber } from '@/utils/format';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
-import { Typography } from 'antd';
+import { Flex, Typography } from 'antd';
import { useEffect, useState } from 'react';
import styles from './index.less';
@@ -27,20 +27,32 @@ function UserPoints() {
return (
-
当前可用算力积分
-
- {userCredit}
-
-
{
- navigate('/points');
- }}
- >
- 查看详情
+
算力积分明细
+
+
+
当前可以积分
+
+
+ {userCredit}
+
+ 分
+
+
+
+
总消耗积分
+
+
+ {userCredit}
+
+ 分
+
+
);
diff --git a/react-ui/src/pages/Workspace/index.less b/react-ui/src/pages/Workspace/index.less
index 7ff7592b..b090799d 100644
--- a/react-ui/src/pages/Workspace/index.less
+++ b/react-ui/src/pages/Workspace/index.less
@@ -1,71 +1,15 @@
.workspace {
position: relative;
height: 100%;
- padding: 20px 30px 10px;
+ padding: 80px 24px 70px;
overflow-y: auto;
- background: linear-gradient(#ecf2fe, #f9fafb);
+ .backgroundFullImage(url(@/assets/img/workspace/workspace-bg.png));
- &__overview {
- flex: 1;
- gap: 15px;
- padding: 20px 30px;
- background-color: white;
- border-radius: 4px;
-
- &__title {
- margin-bottom: 20px;
- color: @text-color;
- font-weight: 500;
- font-size: @font-size-title;
- }
-
- &__content {
- display: flex;
- flex-wrap: wrap;
- gap: 15px;
- align-items: center;
-
- &__statistics {
- flex: none;
- // min-width: 500px;
- background: linear-gradient(
- 123.08deg,
- rgba(138, 138, 138, 0.06) 1.32%,
- rgba(22, 100, 255, 0.02) 58.35%
- );
- border-radius: 4px;
-
- // // 媒体查询
- // @media screen and (max-width: 1600px) {
- // flex: 1;
- // }
- }
- }
- }
-
- &__quick-start {
- display: flex;
- gap: 15px;
- align-items: flex-start;
- width: 100%;
- margin-top: 16px;
- }
-
- &__user {
+ &__right {
display: flex;
flex-direction: column;
- width: 326px;
- min-width: 326px;
- height: 700px;
- }
-
- &__robot-img {
- position: fixed;
- right: 20px;
- bottom: 90px;
- z-index: 99;
- width: 56px;
- height: 56px;
- cursor: pointer;
+ width: calc(25% - 1.25rem);
+ padding: 1.25rem;
+ .backgroundFullImage(url(@/assets/img/workspace/points-bg.png));
}
}
diff --git a/react-ui/src/pages/Workspace/index.tsx b/react-ui/src/pages/Workspace/index.tsx
index 5da8f2fe..166c697e 100644
--- a/react-ui/src/pages/Workspace/index.tsx
+++ b/react-ui/src/pages/Workspace/index.tsx
@@ -2,17 +2,18 @@
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';
-import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart';
-import ExperitableTable from './components/ExperimentTable';
-import QuickStart from './components/QuickStart';
+import { type ExperimentStatistics } from './components/ExperimentChart';
// import RobotFrame from './components/RobotFrame';
-import TotalStatistics from './components/TotalStatistics';
+import { Flex } from 'antd';
+import AssetsManagement from './components/AssetsManagement';
+import ExperimentChart from './components/ExperimentChart';
+import Experiments from './components/Experiments';
+import Messages from './components/Messages';
+import Notebooks from './components/Notebooks';
+import WorkspaceSearch from './components/Search';
import UserPoints from './components/UserPoints';
-import WorkspaceIntro from './components/WorkspaceIntro';
import styles from './index.less';
type OverviewData = {
@@ -24,11 +25,6 @@ type OverviewData = {
function Workspace() {
const [overviewData, setOverviewData] = useState
();
- // const [robotFrameVisible, setRobotFrameVisible] = useState(false);
- // const { handleStart, handleStop, handleDrag, handleClick } = useDraggable(() =>
- // setRobotFrameVisible((prev) => !prev),
- // );
- const users: number[] = new Array(8).fill(0);
useEffect(() => {
getWorkspaceOverview();
@@ -44,59 +40,23 @@ function Workspace() {
return (
-
-
-
-
运行概览
-
-
-
-
-
-
-
- {overviewData?.experimentInsStatus && (
-
- )}
-
-
-
-
-
-
- {/*
-
-
-
-
setRobotFrameVisible(false)}
- > */}
+
+
+ {overviewData?.experimentInsStatus && (
+
+ )}
+
+
+
);
}