From fe53a64d184b256e7307cb77c385ac27e5e91b96 Mon Sep 17 00:00:00 2001 From: Shenhan11 Date: Wed, 18 Mar 2026 19:59:27 +0800 Subject: [PATCH] front end UI style and experience optimization Signed-off-by: Shenhan11 --- packages/web/package.json | 3 + .../web/projects/vgpu/components/Detail.vue | 45 +- .../web/projects/vgpu/components/TabTop.vue | 262 +++++-- .../web/projects/vgpu/components/config.js | 104 +-- .../web/projects/vgpu/components/gauge.vue | 93 ++- .../projects/vgpu/components/previewBar.vue | 137 ++-- .../projects/vgpu/hooks/useInstantVector.js | 35 +- .../projects/vgpu/hooks/useLocalPagination.js | 35 + .../vgpu/hooks/useTableColumnVisibility.js | 54 ++ .../projects/vgpu/hooks/useTableFilters.js | 20 + .../projects/vgpu/views/card/admin/Detail.vue | 692 ++++++++++++++---- .../vgpu/views/card/admin/getOptions.js | 63 +- .../projects/vgpu/views/card/admin/index.vue | 505 +++++++++++-- .../vgpu/views/card/admin/searchSchema.js | 34 - .../vgpu/views/monitor/overview/Block.vue | 12 +- .../vgpu/views/monitor/overview/config.js | 154 +--- .../vgpu/views/monitor/overview/getOptions.js | 94 ++- .../vgpu/views/monitor/overview/index.vue | 356 ++++----- .../vgpu/views/monitor/overview/style.scss | 537 ++++---------- .../projects/vgpu/views/node/admin/Detail.vue | 549 ++++++++++---- .../vgpu/views/node/admin/getOptions.js | 54 +- .../projects/vgpu/views/node/admin/index.vue | 398 ++++++---- .../vgpu/views/node/admin/searchSchema.js | 41 -- .../projects/vgpu/views/task/admin/Detail.vue | 358 +++++---- .../projects/vgpu/views/task/admin/config.js | 3 + .../projects/vgpu/views/task/admin/index.vue | 384 +++++++--- .../vgpu/views/task/admin/searchSchema.js | 60 -- .../projects/vgpu/views/task/admin/top.vue | 2 +- .../web/src/assets/assets-compact-active.svg | 3 + .../src/assets/assets-compact-inactive.svg | 3 + packages/web/src/assets/icons/resource.svg | 22 + .../web/src/assets/logo/hami-graph-color.svg | 13 + packages/web/src/assets/logo/hami-logo.svg | 4 +- packages/web/src/components/BackHeader.vue | 14 +- packages/web/src/components/BlockBox.vue | 10 +- packages/web/src/components/EllipsisText.vue | 136 ++++ .../web/src/components/FormPlus/FormItem.vue | 85 +-- .../web/src/components/FormPlusDrawer.vue | 2 +- .../web/src/components/LangSelect/index.vue | 12 +- packages/web/src/components/ListHeader.vue | 6 +- .../src/components/TablePlus/Pagination.vue | 97 ++- .../src/components/TablePlus/SearchTag.vue | 48 -- .../web/src/components/TablePlus/Toolbar.vue | 411 +++-------- .../web/src/components/TablePlus/index.vue | 532 -------------- .../web/src/components/TablePlus/style.scss | 108 --- packages/web/src/components/TextPlus.vue | 41 +- packages/web/src/components/TimePicker.vue | 29 +- .../web/src/components/TimeRangePicker.vue | 116 +++ .../web/src/components/TrendTimeFilter.vue | 139 ++++ packages/web/src/components/UserCard.vue | 4 +- packages/web/src/components/index.js | 8 +- packages/web/src/icons/svg/card-id.svg | 13 + packages/web/src/icons/svg/jump.svg | 3 + packages/web/src/icons/svg/node-name.svg | 13 + packages/web/src/icons/svg/resource.svg | 5 + .../web/src/icons/svg/status-schedulable.svg | 5 + .../web/src/icons/svg/status-unmanaged.svg | 5 + .../src/icons/svg/status-unschedulable.svg | 5 + packages/web/src/icons/svg/task-name.svg | 11 + packages/web/src/icons/svg/vgpu-gpu-d.svg | 13 +- packages/web/src/icons/svg/vgpu-mem.svg | 24 +- packages/web/src/icons/svg/vgpu-node.svg | 7 +- packages/web/src/icons/svg/vgpu-resource.svg | 4 + .../web/src/layout/components/AppMain.vue | 93 ++- .../src/layout/components/Sidebar/index.vue | 368 +++------- .../src/layout/components/Sidebar/menu.scss | 217 ++++++ .../src/layout/components/TopBar/index.vue | 2 +- .../web/src/layout/components/TopBar/logo.vue | 58 +- .../src/layout/components/TopBar/style.scss | 2 +- packages/web/src/layout/index.vue | 175 +++-- packages/web/src/locales/en.js | 90 ++- packages/web/src/locales/zh.js | 92 ++- packages/web/src/main.js | 2 + packages/web/src/plugins/tdesign.js | 6 + packages/web/src/styles/index.scss | 38 +- packages/web/src/styles/table.scss | 104 +++ packages/web/src/utils/index.js | 10 + pnpm-lock.yaml | 81 +- src/app.controller.ts | 3 +- 79 files changed, 4778 insertions(+), 3598 deletions(-) create mode 100644 packages/web/projects/vgpu/hooks/useLocalPagination.js create mode 100644 packages/web/projects/vgpu/hooks/useTableColumnVisibility.js create mode 100644 packages/web/projects/vgpu/hooks/useTableFilters.js delete mode 100644 packages/web/projects/vgpu/views/card/admin/searchSchema.js delete mode 100644 packages/web/projects/vgpu/views/node/admin/searchSchema.js delete mode 100644 packages/web/projects/vgpu/views/task/admin/searchSchema.js create mode 100644 packages/web/src/assets/assets-compact-active.svg create mode 100644 packages/web/src/assets/assets-compact-inactive.svg create mode 100644 packages/web/src/assets/icons/resource.svg create mode 100644 packages/web/src/assets/logo/hami-graph-color.svg create mode 100644 packages/web/src/components/EllipsisText.vue delete mode 100644 packages/web/src/components/TablePlus/SearchTag.vue delete mode 100644 packages/web/src/components/TablePlus/index.vue delete mode 100644 packages/web/src/components/TablePlus/style.scss create mode 100644 packages/web/src/components/TimeRangePicker.vue create mode 100644 packages/web/src/components/TrendTimeFilter.vue create mode 100644 packages/web/src/icons/svg/card-id.svg create mode 100644 packages/web/src/icons/svg/jump.svg create mode 100644 packages/web/src/icons/svg/node-name.svg create mode 100644 packages/web/src/icons/svg/resource.svg create mode 100644 packages/web/src/icons/svg/status-schedulable.svg create mode 100644 packages/web/src/icons/svg/status-unmanaged.svg create mode 100644 packages/web/src/icons/svg/status-unschedulable.svg create mode 100644 packages/web/src/icons/svg/task-name.svg create mode 100644 packages/web/src/icons/svg/vgpu-resource.svg create mode 100644 packages/web/src/layout/components/Sidebar/menu.scss create mode 100644 packages/web/src/plugins/tdesign.js create mode 100644 packages/web/src/styles/table.scss diff --git a/packages/web/package.json b/packages/web/package.json index 3d00ba21..d8b57a8a 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -28,6 +28,7 @@ "bootstrap": "5.3.1", "core-js": "^3.8.3", "crypto-browserify": "^3.12.0", + "dayjs": "^1.11.20", "echarts": "5.4.3", "element-plus": "^2.4.1", "generate-avatar": "1.4.10", @@ -39,6 +40,8 @@ "nprogress": "0.2.0", "socket.io-client": "^4.7.4", "sortablejs": "^1.15.2", + "tdesign-icons-vue-next": "^0.4.2", + "tdesign-vue-next": "^1.18.5", "tom-select": "2.2.2", "vue": "^3.2.13", "vue-clipboard3": "2.0.0", diff --git a/packages/web/projects/vgpu/components/Detail.vue b/packages/web/projects/vgpu/components/Detail.vue index 3257eacd..3c0636b9 100644 --- a/packages/web/projects/vgpu/components/Detail.vue +++ b/packages/web/projects/vgpu/components/Detail.vue @@ -57,16 +57,13 @@ - diff --git a/packages/web/projects/vgpu/components/config.js b/packages/web/projects/vgpu/components/config.js index 6da7771c..18ed5050 100644 --- a/packages/web/projects/vgpu/components/config.js +++ b/packages/web/projects/vgpu/components/config.js @@ -1,4 +1,5 @@ import { timeParse } from '@/utils'; +import i18n from '@/locales'; export default ({ percent, title, unit = '%' }) => { const value = percent.toFixed(1); @@ -31,7 +32,7 @@ export default ({ percent, title, unit = '%' }) => { axisLine: { lineStyle: { width: 8, - backgroundColor: '#F5F7FA', + color: '#F5F7FA', }, }, axisTick: { @@ -110,48 +111,51 @@ export default ({ percent, title, unit = '%' }) => { }; export const getPreviewBarPie = (statusConfig, { title }) => { + const unit = i18n.global.t('common.unitSheet'); + const suffix = unit ? ` ${unit}` : ''; + + const dataList = statusConfig.map((item) => ({ + ...item, + value: Number(item.value || 0), + itemStyle: { + color: item.color, + borderRadius: 6, + borderColor: '#fff', + borderWidth: 2, + }, + })); + return { tooltip: { - show: false, + trigger: 'item', + confine: true, + formatter: (params) => { + return `${params.name}: ${params.value}${suffix}`; + }, }, - // legend: { - // top: '5%', - // left: 'center', - // }, - series: [ { - name: 'Access From', + name: title, type: 'pie', - radius: ['50%', '65%'], + radius: ['48%', '72%'], + center: ['50%', '50%'], avoidLabelOverlap: false, - itemStyle: { - borderRadius: 3, - borderColor: '#fff', - }, label: { show: false, - position: 'center', - }, - emphasis: { - label: { - show: false, - fontSize: 40, - fontWeight: 'bold', - }, }, labelLine: { show: false, }, - // data: [ - // { value: data.running, itemStyle: {color: '#ff0000'} }, - // { value: data.stopped , itemStyle: {color: '#ff0000'}}, - // { value: data.error , itemStyle: {color: '#ff0000'}}, - // ], - data: statusConfig.map((item) => ({ - ...item, - itemStyle: { color: item.color }, - })), + emphasis: { + scale: true, + scaleSize: 4, + itemStyle: { + shadowBlur: 12, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.15)', + }, + }, + data: dataList, }, ], grid: { @@ -160,18 +164,6 @@ export const getPreviewBarPie = (statusConfig, { title }) => { left: 1, // 左边距 right: 1, // 右边距 }, - graphic: [ - { - type: 'text', // 添加文本标签 - left: 'center', // 文本标签水平居中 - top: 'center', // 文本标签垂直居中 - style: { - text: title, // 设置文本内容 - fill: '#333', // 文字颜色 - fontSize: 12, // 文字大小 - }, - }, - ], }; }; @@ -211,11 +203,17 @@ export const getTopOptions = ({ core, memory }) => { name: '算力', type: 'bar', data: core.data, + itemStyle: { + color: '#4F98CA', + }, }, { name: '显存', type: 'bar', data: memory.data, + itemStyle: { + color: '#4F98CA', + }, }, ], }; @@ -261,28 +259,6 @@ export const getLineOptions = ({ data = [], unit = '%' }) => { return item.value.toFixed(1); }), type: 'line', - areaStyle: { - normal: { - color: { - type: 'linear', - x: 0, // 渐变起始点 0% - y: 0, // 渐变起始点 0% - x2: 0, // 渐变结束点 100% - y2: 1, // 渐变结束点 100% - colorStops: [ - { - offset: 0, - color: 'rgba(84, 112, 198, 0.16)', // 渐变起始颜色 - }, - { - offset: 1, - color: 'rgba(84, 112, 198, 0.00)', // 渐变结束颜色 - }, - ], - global: false, // 缺省为 false - }, - }, - }, }, ], }; diff --git a/packages/web/projects/vgpu/components/gauge.vue b/packages/web/projects/vgpu/components/gauge.vue index 24d48b61..a0ebc818 100644 --- a/packages/web/projects/vgpu/components/gauge.vue +++ b/packages/web/projects/vgpu/components/gauge.vue @@ -1,23 +1,30 @@ - - diff --git a/packages/web/projects/vgpu/components/previewBar.vue b/packages/web/projects/vgpu/components/previewBar.vue index 9c835695..2235ca17 100644 --- a/packages/web/projects/vgpu/components/previewBar.vue +++ b/packages/web/projects/vgpu/components/previewBar.vue @@ -9,13 +9,12 @@
  • - +
  • +
  • -
  • - +
  • + +
  • +
  • + +
  • +
  • +
@@ -48,8 +53,8 @@ - diff --git a/packages/web/projects/vgpu/views/card/admin/getOptions.js b/packages/web/projects/vgpu/views/card/admin/getOptions.js index 3044839b..5c3cde3c 100644 --- a/packages/web/projects/vgpu/views/card/admin/getOptions.js +++ b/packages/web/projects/vgpu/views/card/admin/getOptions.js @@ -1,9 +1,12 @@ import { timeParse } from '@/utils'; +import i18n from '@/locales'; export const getRangeOptions = ({ core = [], memory = [] }) => { return { legend: { // data: [], + bottom: 0, + left: 'center', }, tooltip: { trigger: 'axis', @@ -25,9 +28,9 @@ export const getRangeOptions = ({ core = [], memory = [] }) => { }, }, grid: { - top: 37, // 上边距 - bottom: 20, // 下边距 - left: '7%', // 左边距 + top: 20, // 上边距 + bottom: 60, // 下边距 + left: '8%', // 左边距 right: 10, // 右边距 }, xAxis: { @@ -36,13 +39,7 @@ export const getRangeOptions = ({ core = [], memory = [] }) => { axisLabel: { formatter: function (value) { return timeParse(value, 'HH:mm'); - // return timeParse(value, 'MM-DD'); }, - // interval: function (index, value) { - // var date = new Date(value); - // - // return date.getHours() % 2 === 0 && date.getMinutes() === 0; - // }, }, }, yAxis: { @@ -56,31 +53,9 @@ export const getRangeOptions = ({ core = [], memory = [] }) => { }, series: [ { - name: '算力', + name: i18n.global.t('dashboard.compute'), data: core, type: 'line', - areaStyle: { - normal: { - color: { - type: 'linear', - x: 0, // 渐变起始点 0% - y: 0, // 渐变起始点 0% - x2: 0, // 渐变结束点 100% - y2: 1, // 渐变结束点 100% - colorStops: [ - { - offset: 0, - color: 'rgba(84, 112, 198, 0.16)', // 渐变起始颜色 - }, - { - offset: 1, - color: 'rgba(84, 112, 198, 0.00)', // 渐变结束颜色 - }, - ], - global: false, // 缺省为 false - }, - }, - }, itemStyle: { color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色 }, @@ -89,31 +64,9 @@ export const getRangeOptions = ({ core = [], memory = [] }) => { }, }, { - name: '显存', + name: i18n.global.t('dashboard.memory'), data: memory, type: 'line', - areaStyle: { - normal: { - color: { - type: 'linear', - x: 0, // 渐变起始点 0% - y: 0, // 渐变起始点 0% - x2: 0, // 渐变结束点 100% - y2: 1, // 渐变结束点 100% - colorStops: [ - { - offset: 0, - color: 'rgba(34, 139, 34, 0.16)', // 渐变起始颜色 - }, - { - offset: 1, - color: 'rgba(34, 139, 34, 0.00)', // 渐变结束颜色 - }, - ], - global: false, // 缺省为 false - }, - }, - }, itemStyle: { color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色 }, diff --git a/packages/web/projects/vgpu/views/card/admin/index.vue b/packages/web/projects/vgpu/views/card/admin/index.vue index becd6563..e3d6aecb 100644 --- a/packages/web/projects/vgpu/views/card/admin/index.vue +++ b/packages/web/projects/vgpu/views/card/admin/index.vue @@ -1,49 +1,138 @@ - + diff --git a/packages/web/projects/vgpu/views/card/admin/searchSchema.js b/packages/web/projects/vgpu/views/card/admin/searchSchema.js deleted file mode 100644 index 903a5288..00000000 --- a/packages/web/projects/vgpu/views/card/admin/searchSchema.js +++ /dev/null @@ -1,34 +0,0 @@ -import nodeApi from '~/vgpu/api/node'; -import cardApi from '~/vgpu/api/card'; - -export default (t) => ({ - items: [ - { - label: t('card.id'), - name: 'uid', - component: 'input', - }, - { - label: t('card.node'), - name: 'nodeName', - component: 'select', - props: { - mode: 'remote', - api: nodeApi.getNodeList({ filters: {} }), - labelKey: 'name', - valueKey: 'name', - }, - }, - { - label: t('card.model'), - name: 'type', - component: 'select', - props: { - mode: 'remote', - api: cardApi.getCardType(), - labelKey: 'type', - valueKey: 'type', - }, - }, - ], -}); diff --git a/packages/web/projects/vgpu/views/monitor/overview/Block.vue b/packages/web/projects/vgpu/views/monitor/overview/Block.vue index 63a5ea64..8f237f54 100644 --- a/packages/web/projects/vgpu/views/monitor/overview/Block.vue +++ b/packages/web/projects/vgpu/views/monitor/overview/Block.vue @@ -14,25 +14,25 @@ defineProps(['title']); - diff --git a/packages/web/projects/vgpu/views/monitor/overview/style.scss b/packages/web/projects/vgpu/views/monitor/overview/style.scss index 8c01b3aa..098cb178 100644 --- a/packages/web/projects/vgpu/views/monitor/overview/style.scss +++ b/packages/web/projects/vgpu/views/monitor/overview/style.scss @@ -1,89 +1,115 @@ .home { display: flex; + flex-direction: column; gap: 16px; height: 100%; + min-width: 0; font-size: 12px; - ul { - margin: 0; - padding: 0; - list-style: none; - } - .home-block { - margin-bottom: 0; + &-page-title { + width: 100%; + flex-shrink: 0; + padding-top: 8px; + font-size: 18px; + font-weight: 500; + color: #1d2b3a; + line-height: 24px; } - .all-btn { - color: #939ea9; - text-align: center; - font-family: 'PingFang SC'; - font-size: 12px; - font-style: normal; - font-weight: 500; - line-height: 20px; /* 166.667% */ + &-top { + display: flex; + gap: 16px; + } - cursor: pointer; - &:hover { - color: var(--el-color-primary); - } + &-top-left { + flex: 7; + min-width: 0; + display: flex; + flex-direction: column; + gap: 16px; } - &-left { - width: 65%; + &-top-right { + flex: 3; + min-width: 0; display: flex; flex-direction: column; gap: 16px; } - &-right { + &-top-right-card.home-block { + display: flex; + flex-direction: column; + } + + &-top-right-card.home-block:first-child { + flex: 0.6; + } + + &-top-right-card.home-block:last-child { + flex: 1.4; + } + + &-top-right-card .home-block-content { flex: 1; - //display: flex; - //flex-direction: column; - //gap: 16px; - //width: 100%; + display: flex; + flex-direction: column; } - .visited { - list-style: none; + .card-type-chart { + flex: 1; + min-height: 0; + } + + &-bottom { display: flex; - gap: 12px; - flex-wrap: wrap; - li { - display: flex; - padding: 8px 16px; - align-items: center; - gap: 8px; - // flex: 1 0 0; - border-radius: 4px; - border: 1px solid #e4ebf1; - background: #fff; + flex-direction: column; + gap: 16px; + } - max-width: 180px; - &:hover { - border: 1px solid var(--el-color-primary-light-3); - color: var(--el-color-primary); - cursor: pointer; - } + &-bottom-trend-filter { + margin-bottom: 0; + .trend-time-filter { + margin-bottom: 0; } } - .visited-empty { - text-align: center; - color: #939ea9; + &-bottom-row { + display: flex; + gap: 16px; + } + + &-bottom-top5-row { + border-top: 1px dashed #d1d9e0; + padding-top: 16px; + } + + &-bottom-col { + flex: 1; + min-width: 0; + } + + ul { + margin: 0; + padding: 0; + list-style: none; + } + + .home-block { + margin-bottom: 0; } - .add-btn { - color: var(--el-color-primary); + .all-btn { + color: #939ea9; text-align: center; - font-family: 'PingFang SC'; font-size: 12px; font-style: normal; font-weight: 500; line-height: 20px; /* 166.667% */ - cursor: pointer; + cursor: pointer; &:hover { - opacity: 0.7; + color: var(--el-color-primary); } } @@ -91,301 +117,65 @@ display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; + li { - height: 80px; + min-height: 80px; flex-shrink: 0; - border-radius: 6px; + border-radius: 8px; background: #f5f7fa; padding: 16px; display: flex; - //&:hover { - // color: var(--el-color-primary); - // cursor: pointer; - //} - .avatar { - display: flex; - width: 48px; - height: 48px; - padding: 14px; - justify-content: center; - align-items: center; - flex-shrink: 0; - border-radius: 999px; - border: 2px solid #fff; - background: linear-gradient( - 180deg, - rgba(255, 255, 255, 0) 0%, - #fff 100% - ); - margin-right: 16px; - } - - .count { - font-family: Roboto; - font-size: 20px; - font-style: normal; - font-weight: 700; - line-height: 100%; /* 20px */ - margin-top: 10px; - } - } - } - - .resourceStatus { - display: flex; - gap: 20px; - min-height: 160px; - - &-left { - flex: 1; - // padding: 10px 20px; - padding-right: 0; - display: flex; align-items: center; - gap: 34px; - .pie { - height: 120px; - width: 120px; - margin-left: 20px; - } - .counts { - flex: 1; + transition: box-shadow 0.2s; - display: flex; - justify-content: space-between; - gap: 8px; - padding-right: 40px; - flex-wrap: wrap; - &-item { - padding: 8px 12px; - display: flex; - flex-direction: column; - justify-content: center; - &-title { - margin-bottom: 10px; - display: flex; - align-items: center; - gap: 4px; - } - - &-count { - color: #1d2b3a; - font-family: Roboto; - font-size: 20px; - font-style: normal; - font-weight: 700; - line-height: 100%; /* 20px */ - } - } + &:hover { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } - } - &-right { - width: 55%; - display: flex; - align-items: center; - gap: 12px; - li { - flex: 1; - min-height: 150px; - border-radius: 6px; - background: #f5f7fa; - padding: 16px; + .avatar { display: flex; - flex-direction: column; + width: 40px; + height: 40px; + box-sizing: border-box; + padding: 10px; justify-content: center; - .title { - display: flex; - gap: 4px; - align-items: center; - } - .progress { - padding: 25px 0; - background-color: transparent; - - .el-progress { - width: 100%; - } - } - .volume { - display: flex; - //flex-direction: column; - gap: 8px; - flex-wrap: wrap; - } - .volume-item { - flex: 1; - display: flex; - align-items: center; - gap: 6px; - font-size: 12px; - min-width: 80px; - - .title { - color: #697886; - font-family: 'PingFang SC'; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 100%; /* 12px */ - //width:80px ; - } - .count { - color: #1d2b3a; - font-family: Roboto; - font-size: 12px; - font-style: normal; - font-weight: 500; - line-height: 100%; - } - } + align-items: center; + flex-shrink: 0; + border-radius: 8px; + background: #fff; + border: none; + box-shadow: 0 2px 8px #0205080a, 0 6px 20px #02050814; + margin-right: 14px; + color: #64748b; } - } - } - #alarm { - } + .main { + flex: 1; + min-width: 0; - .alarm { - display: flex; - gap: 32px; - &-left { - display: flex; - flex-direction: column; - gap: 12px; - &-item { - width: 202px; - height: 120px; - border-radius: 6px; - padding: 16px; - position: relative; .title { - color: #324558; - font-family: 'PingFang SC'; font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 18px; /* 150% */ - margin-bottom: 10px; - } - .count { - font-family: Roboto; - font-size: 20px; - font-style: normal; - font-weight: 700; - line-height: 100%; /* 20px */ - } - .icon-bg { - position: absolute; - right: 10px; - bottom: -10px; - width: 56px; - height: 56px; + color: #939ea9; + line-height: 18px; + margin-top: 4px; } } - } - &-right { - flex: 1; - position: relative; - .title { - position: absolute; - top: -40px; - color: #1d2b3a; - font-family: 'PingFang SC'; - font-size: 14px; - font-style: normal; + .count { + font-family: Roboto, -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 16px; font-weight: 500; - line-height: 20px; /* 142.857% */ - } - } - } - - .pressure { - display: flex; - height: 150px; - li { - flex: 1; - } - } - - .user { - border: 1px solid #fff; - padding: 0; - } + line-height: 28px; + color: #324558; - .myApproval { - display: flex; - li { - display: flex; - padding: 8px 0px; - flex-direction: column; - align-items: center; - gap: 8px; - flex: 1 0 0; - &:hover { - color: var(--el-color-primary); - cursor: pointer; - } - .count { - font-family: Roboto; - font-size: 20px; - font-style: normal; - font-weight: 700; - line-height: 100%; /* 20px */ - position: relative; - .isNew { - display: inline-flex; - min-width: 20px; - padding: 0px 6px; - flex-direction: column; - justify-content: center; - align-items: center; - border-radius: 6px 6px 6px 2px; - background: var(--light-color-semantic-danger-default, #dc2626); - color: #fff; - text-align: center; - font-family: Roboto; - font-size: 12px; - font-style: normal; - font-weight: 500; - line-height: 20px; /* 166.667% */ - position: absolute; - right: -40px; - top: -8px; + .count-unit { + font: inherit; + color: inherit; } } } } - .myApply { - display: grid; - gap: 12px; - grid-template-columns: repeat(2, 1fr); - - li { - display: flex; - // width: 202px; - padding: 16px 16px; - align-items: center; - gap: 8px; - border-radius: 4px; - background: #f5f7fa; - justify-content: space-between; - &:hover { - color: var(--el-color-primary); - cursor: pointer; - } - .count { - font-family: Roboto; - font-size: 14px; - font-style: normal; - font-weight: 700; - line-height: 20px; /* 142.857% */ - } - } - } - .node-all { display: grid; gap: 12px; @@ -393,107 +183,32 @@ li { display: flex; - // width: 202px; padding: 16px 16px; align-items: center; - gap: 8px; - border-radius: 4px; - background: #f5f7fa; justify-content: space-between; - &:hover { - color: var(--el-color-primary); - cursor: pointer; - } - .count { - font-family: Roboto; - font-size: 14px; - font-style: normal; - font-weight: 700; - line-height: 20px; /* 142.857% */ - } - } - } - - .card-resource { - display: flex; -//padding: 0 20px; - min-height: 120px; - .left { - flex: 1; - min-width: 402px; - - .pie { - width: 100%; - height: 100%; - } - } - - .cardDetail { - flex: 1; - display: flex; - flex-wrap: wrap; - //grid-template-columns: repeat(3, 1fr); gap: 12px; - align-content: center; - } - .cardDetail-item { - flex: 1; - height: 80px; - flex-shrink: 0; - border-radius: 6px; + border-radius: 8px; background: #f5f7fa; - padding: 16px; - display: flex; + transition: box-shadow 0.2s; + cursor: pointer; + &:hover { - //color: var(--el-color-primary); - //cursor: pointer; - } - .avatar { - display: flex; - width: 48px; - height: 48px; - padding: 14px; - justify-content: center; - align-items: center; - flex-shrink: 0; - border-radius: 999px; - border: 2px solid #fff; - background: linear-gradient( - 180deg, - rgba(255, 255, 255, 0) 0%, - #fff 100% - ); - margin-right: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } - .count { - font-family: Roboto; - font-size: 20px; - font-style: normal; - font-weight: 700; - line-height: 100%; /* 20px */ - margin-top: 10px; + .title { + font-size: 13px; + color: #324558; + line-height: 20px; } - } - } - .exceed { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 12px; - li { - height: 80px; - border-radius: 4px; - background: #f5f7fa; - padding: 16px; .count { - font-size: 20px; - font-weight: bold; - margin-top: 5px; - span { - font-size: 12px; - } + font-family: Roboto, -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 18px; + font-weight: 600; + line-height: 24px; } } } + } diff --git a/packages/web/projects/vgpu/views/node/admin/Detail.vue b/packages/web/projects/vgpu/views/node/admin/Detail.vue index 98fed427..660c195c 100644 --- a/packages/web/projects/vgpu/views/node/admin/Detail.vue +++ b/packages/web/projects/vgpu/views/node/admin/Detail.vue @@ -1,61 +1,141 @@ diff --git a/packages/web/src/layout/components/Sidebar/menu.scss b/packages/web/src/layout/components/Sidebar/menu.scss new file mode 100644 index 00000000..909c5acd --- /dev/null +++ b/packages/web/src/layout/components/Sidebar/menu.scss @@ -0,0 +1,217 @@ +.sidebar { + width: 100%; + box-sizing: border-box; + + .side-container { + position: relative; + width: 100%; + height: 100%; + padding: 0 0 8px; + background-color: #f5f7fa; + + .side-logo { + padding: 8px 12px 8px 24px; + display: flex; + align-items: center; + justify-content: flex-start; + border-bottom: none; + position: relative; + + &::after { + content: ''; + position: absolute; + left: 12px; + right: 12px; + bottom: 0; + height: 1px; + background: #e4ebf1; + } + } + + .side-logo-main { + flex: 1; + cursor: pointer; + display: flex; + align-items: center; + min-height: 34px; + } + + .logo-expanded { + display: flex; + align-items: center; + } + + .side-menus { + box-sizing: border-box; + height: calc(100% - 90px); + padding-top: 10px; + overflow: auto; + overflow-x: hidden; + + :deep(.t-default-menu) { + background: transparent; + width: 100%; + height: 100%; + } + + :deep(.t-default-menu__inner .t-menu) { + padding: 0 12px; + } + + :deep(.t-menu__item) { + border-radius: 6px; + } + + :deep(.t-menu__item .t-icon), + :deep(.t-submenu .t-menu__item .t-icon) { + width: 16px; + height: 16px; + } + + :deep(.t-submenu > .t-menu__item .t-fake-arrow.t-fake-arrow--active path) { + d: path('M3.75 5.7998L7.99274 10.0425L12.2361 5.79921'); + } + + :deep(.t-submenu > .t-menu__item .t-fake-arrow.t-fake-arrow--transform.t-fake-arrow--active path) { + transform: none !important; + } + + :deep(.t-menu__item:hover:not(.t-is-active):not(.t-is-opened):not(.t-is-disabled)), + :deep(.t-submenu > .t-menu__item:hover:not(.t-is-disabled)), + :deep(.t-submenu .t-menu__sub .t-menu__item:hover:not(.t-is-active):not(.t-is-disabled)) { + background: #fff; + box-shadow: + 0 1px 1px 0 rgb(2 5 8 / 2%), + 0 1px 4px 0 rgb(2 5 8 / 6%); + } + + :deep(.t-submenu .t-menu__sub) { + position: relative; + } + + :deep(.t-submenu .t-menu__sub::before) { + content: ' '; + display: block; + width: 1px; + height: calc(100% - 36px); + position: absolute; + top: 50%; + left: 22px; + z-index: 9; + background: #d5dee7; + transform: translateY(-50%); + } + + :deep(.t-submenu .t-menu__sub .t-menu__item) { + padding: 0 0 0 36px; + position: relative; + color: #324558; + } + + :deep(.t-submenu .t-menu__sub .t-menu__item::before) { + content: ' '; + position: absolute; + top: 50%; + left: 20px; + z-index: 9; + width: 5px; + height: 5px; + background-color: #d5dee7; + border-radius: 4px; + transform: translateY(-50%); + } + + :deep(.t-submenu .t-menu__sub .t-menu__item.t-is-active:not(.t-is-opened)::before) { + background-color: #2563eb; + } + + :deep(.t-submenu .t-menu__sub .t-menu__item.t-is-active:not(.t-is-opened)) { + color: #2563eb; + background: #fff; + box-shadow: + 0 1px 1px 0 rgb(2 5 8 / 2%), + 0 1px 4px 0 rgb(2 5 8 / 6%); + } + + :deep(.t-menu__item.t-is-active:not(.t-is-opened)) { + color: #0052d9; + background: #fff; + box-shadow: + 0 1px 1px 0 rgb(2 5 8 / 2%), + 0 1px 4px 0 rgb(2 5 8 / 6%); + font-weight: 400; + } + + :deep(.t-menu__item.t-is-active.t-is-opened) { + color: #0052d9; + background: transparent; + box-shadow: none; + } + + :deep(.t-is-opened) { + &:has(.t-is-active) { + .t-is-active { + color: #0052d9; + + svg { + fill: #0052d9; + } + } + } + } + + :deep(.resource-submenu > .t-menu__item .side-resource-icon) { + color: #697886; + } + + :deep(.resource-submenu-child-active > .t-menu__item) { + color: #0052d9; + } + + :deep(.resource-submenu-child-active > .t-menu__item .side-resource-icon) { + color: #0052d9; + } + + :deep( + .resource-submenu-child-active + > .t-menu__item:not(.t-is-opened):not(.t-is-disabled), + .resource-submenu-child-active + > .t-menu__item:not(.t-is-opened):not(.t-is-disabled):hover + ) { + background: #f2f3ff; + box-shadow: none; + } + } + + .logo-compact-graph { + display: block; + width: 34px; + height: 34px; + flex-shrink: 0; + object-fit: contain; + } + + &.is-collapsed { + .side-logo { + padding: 6px 12px; + justify-content: center; + + &::after { + left: 12px; + right: 12px; + } + } + + .side-logo-main { + flex: 0 0 auto; + width: 100%; + justify-content: center; + } + + .logo-compact-graph { + width: 28px; + height: 28px; + } + } + } +} diff --git a/packages/web/src/layout/components/TopBar/index.vue b/packages/web/src/layout/components/TopBar/index.vue index 71b8d407..9f3f5391 100644 --- a/packages/web/src/layout/components/TopBar/index.vue +++ b/packages/web/src/layout/components/TopBar/index.vue @@ -1,7 +1,7 @@