refactor: Dashboard 看板布局与图表优化
DashboardView 调整看板卡片与栅格展示,ECharts 补充图表类型,主布局 dashboard 区域由内嵌滚动改为自适应可滚动以避免内容被裁剪,回归脚本适配。
This commit is contained in:
@@ -19,6 +19,8 @@ assert.match(
|
|||||||
|
|
||||||
assert.match(echartsSource, /import\s*\{[^}]*BarChart[^}]*\}\s*from\s*['"]echarts\/charts['"]/, 'ECharts 未注册 BarChart')
|
assert.match(echartsSource, /import\s*\{[^}]*BarChart[^}]*\}\s*from\s*['"]echarts\/charts['"]/, 'ECharts 未注册 BarChart')
|
||||||
assert.match(echartsSource, /use\(\[[\s\S]*?BarChart[\s\S]*?\]\)/, 'BarChart 未加入 ECharts 按需注册列表')
|
assert.match(echartsSource, /use\(\[[\s\S]*?BarChart[\s\S]*?\]\)/, 'BarChart 未加入 ECharts 按需注册列表')
|
||||||
|
assert.match(echartsSource, /import\s*\{[^}]*PieChart[^}]*\}\s*from\s*['"]echarts\/charts['"]/, 'ECharts 未注册 PieChart')
|
||||||
|
assert.match(echartsSource, /use\(\[[\s\S]*?PieChart[\s\S]*?\]\)/, 'PieChart 未加入 ECharts 按需注册列表')
|
||||||
|
|
||||||
for (const copy of [
|
for (const copy of [
|
||||||
'平台运行状态',
|
'平台运行状态',
|
||||||
@@ -39,16 +41,24 @@ assert.match(dashboardSource, /router\.push\(['"]\/fine-tune['"]\)/, '查看全
|
|||||||
assert.match(dashboardSource, /router\.push\(`\/training-log\/\$\{task\.id\}`\)/, '训练任务详情应进入训练日志页')
|
assert.match(dashboardSource, /router\.push\(`\/training-log\/\$\{task\.id\}`\)/, '训练任务详情应进入训练日志页')
|
||||||
assert.doesNotMatch(dashboardSource, /class=["']dashboard-heading["']/, '服务看板不应重复展示页面标题栏')
|
assert.doesNotMatch(dashboardSource, /class=["']dashboard-heading["']/, '服务看板不应重复展示页面标题栏')
|
||||||
assert.doesNotMatch(dashboardSource, /查看告警/, '服务看板不应保留冗余的顶部告警按钮')
|
assert.doesNotMatch(dashboardSource, /查看告警/, '服务看板不应保留冗余的顶部告警按钮')
|
||||||
assert.match(dashboardSource, /\.dashboard-view\s*\{[\s\S]*?gap:\s*10px;/, '服务看板应使用紧凑的区块间距')
|
assert.match(dashboardSource, /\.dashboard-view\s*\{[\s\S]*?gap:\s*16px;/, '服务看板区块间距应保持舒展')
|
||||||
assert.match(dashboardSource, /\.dashboard-middle\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?min-height:\s*0;/, '中间区域应弹性占满剩余高度')
|
assert.match(dashboardSource, /\.dashboard-view\s*\{[\s\S]*?min-height:\s*100%;/, '服务看板应至少填满页面可用高度')
|
||||||
|
assert.match(dashboardSource, /\.dashboard-middle\s*\{[\s\S]*?flex:\s*0 0 auto;[\s\S]*?min-height:\s*0;/, '中间区域不应因新增统计卡片被压缩')
|
||||||
assert.match(dashboardSource, /grid-template-columns:\s*minmax\(0,\s*1\.9fr\)\s*minmax\(300px,\s*0\.82fr\);/, '服务状态列应收窄,为训练图表释放更多宽度')
|
assert.match(dashboardSource, /grid-template-columns:\s*minmax\(0,\s*1\.9fr\)\s*minmax\(300px,\s*0\.82fr\);/, '服务状态列应收窄,为训练图表释放更多宽度')
|
||||||
assert.match(dashboardSource, /\.training-chart\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?height:\s*auto;[\s\S]*?min-height:\s*230px;/, '训练统计图应随可用空间自动拉伸')
|
assert.match(dashboardSource, /\.training-chart\s*\{[\s\S]*?height:\s*300px;[\s\S]*?min-height:\s*300px;/, '训练统计图应保持舒展、稳定的展示高度')
|
||||||
assert.match(dashboardSource, /@media\s*\(max-height:\s*900px\)[\s\S]*?\.training-chart\s*\{[\s\S]*?min-height:\s*200px;/, '较矮桌面视口应保留图表最小可读高度')
|
|
||||||
assert.match(dashboardSource, /\.service-table\s*\{[\s\S]*?grid-template-rows:[^;]*repeat\(4,\s*minmax\(48px,\s*1fr\)\)/, '服务状态行应随中间区域同步拉伸')
|
assert.match(dashboardSource, /\.service-table\s*\{[\s\S]*?grid-template-rows:[^;]*repeat\(4,\s*minmax\(48px,\s*1fr\)\)/, '服务状态行应随中间区域同步拉伸')
|
||||||
assert.match(dashboardSource, /\.tasks-heading\s*\{[\s\S]*?min-height:\s*32px;[\s\S]*?padding:\s*0 14px 10px;/, '训练任务标题区应适当加高')
|
assert.match(dashboardSource, /\.tasks-heading\s*\{[\s\S]*?min-height:\s*32px;[\s\S]*?padding:\s*0 14px 10px;/, '训练任务标题区应适当加高')
|
||||||
assert.match(dashboardSource, /\.tasks-table\s*\{[\s\S]*?th,\s*td\s*\{[\s\S]*?height:\s*42px;[\s\S]*?th\s*\{[\s\S]*?height:\s*36px;/, '训练任务表格行应适当加高')
|
assert.match(dashboardSource, /\.tasks-table\s*\{[\s\S]*?th,\s*td\s*\{[\s\S]*?height:\s*42px;[\s\S]*?th\s*\{[\s\S]*?height:\s*36px;/, '训练任务表格行应适当加高')
|
||||||
assert.match(dashboardSource, /@media\s*\(max-height:\s*900px\)[\s\S]*?\.tasks-heading\s*\{[\s\S]*?min-height:\s*28px;[\s\S]*?padding:\s*0 12px 8px;[\s\S]*?\.tasks-table\s*\{[\s\S]*?height:\s*36px;[\s\S]*?th\s*\{[\s\S]*?height:\s*32px;/, '低高度桌面视口也应提高训练任务占比')
|
assert.match(dashboardSource, /@media\s*\(max-height:\s*900px\)[\s\S]*?\.tasks-heading\s*\{[\s\S]*?min-height:\s*28px;[\s\S]*?padding:\s*0 12px 8px;[\s\S]*?\.tasks-table\s*\{[\s\S]*?height:\s*36px;[\s\S]*?th\s*\{[\s\S]*?height:\s*32px;/, '低高度桌面视口也应提高训练任务占比')
|
||||||
assert.match(mainLayoutSource, /\.layout-content:has\(\.dashboard-view\)[\s\S]*?overflow-y:\s*hidden;/, '服务看板容器应避免产生页面纵向滚动条')
|
assert.match(dashboardSource, /class=["']user-stats-row["'][\s\S]*?用户操作分布[\s\S]*?登录时长排行[\s\S]*?最近登录用户/, '服务看板应展示三块用户统计卡片')
|
||||||
assert.match(mainLayoutSource, /\.page-canvas\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?overflow:\s*hidden;/, '服务看板页面画布应约束在可用视口内')
|
assert.match(dashboardSource, /class=["']duration-chart["'][\s\S]*?loginDurationChartOption/, '登录时长应使用 ECharts 图表展示')
|
||||||
|
assert.match(dashboardSource, /const loginDurationChartOption[\s\S]*?type:\s*['"]bar['"][\s\S]*?formatter:\s*['"]\{c\} 小时['"]/, '登录时长应以横向柱状图展示具体小时数')
|
||||||
|
assert.match(dashboardSource, /const operationChartOption[\s\S]*?position:\s*['"]outside['"][\s\S]*?labelLine:\s*\{[\s\S]*?show:\s*true/, '饼图应以外侧引导线标注操作名称')
|
||||||
|
assert.match(dashboardSource, /const loginDurationChartOption[\s\S]*?grid:\s*\{\s*top:\s*8,\s*right:\s*12,\s*bottom:\s*6,\s*left:\s*8,[\s\S]*?max:\s*Math\.ceil\(Math\.max[\s\S]*?position:\s*['"]insideRight['"]/, '登录时长图应收紧左右边距、按数据范围拉伸,并将数值置于柱内')
|
||||||
|
assert.match(dashboardSource, /\.user-stats-row\s*\{[\s\S]*?repeat\(3,\s*minmax\(0,\s*1fr\)\)/, '宽屏用户统计卡片应保持三列')
|
||||||
|
assert.match(dashboardSource, /@media\s*\(max-width:\s*1180px\)[\s\S]*?\.user-stats-row\s*\{[\s\S]*?repeat\(2,\s*minmax\(0,\s*1fr\)\)/, '中等宽度应将用户统计卡片降为两列')
|
||||||
|
assert.match(dashboardSource, /@media\s*\(max-width:\s*720px\)[\s\S]*?\.user-stats-row\s*\{[\s\S]*?grid-template-columns:\s*1fr;/, '窄屏应将用户统计卡片降为单列')
|
||||||
|
assert.match(mainLayoutSource, /\.layout-content:has\(\.dashboard-view\)[\s\S]*?overflow-y:\s*auto;/, '服务看板应允许在内容超出视口时纵向滚动')
|
||||||
|
assert.match(mainLayoutSource, /\.page-canvas\s*\{[\s\S]*?flex:\s*1 0 auto;[\s\S]*?overflow:\s*visible;/, '服务看板画布应允许新增卡片完整显示')
|
||||||
|
|
||||||
console.log('服务看板回归检查通过')
|
console.log('服务看板回归检查通过')
|
||||||
|
|||||||
@@ -84,13 +84,13 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
@media (min-width: 1181px) {
|
@media (min-width: 1181px) {
|
||||||
.layout-content:has(.dashboard-view) {
|
.layout-content:has(.dashboard-view) {
|
||||||
overflow-y: hidden;
|
overflow-y: auto;
|
||||||
|
|
||||||
.page-canvas {
|
.page-canvas {
|
||||||
flex: 1 1 auto;
|
flex: 1 0 auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { use } from 'echarts/core'
|
import { use } from 'echarts/core'
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
import { BarChart, LineChart } from 'echarts/charts'
|
import { BarChart, LineChart, PieChart } from 'echarts/charts'
|
||||||
import {
|
import {
|
||||||
GridComponent,
|
GridComponent,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
@@ -17,6 +17,7 @@ use([
|
|||||||
CanvasRenderer,
|
CanvasRenderer,
|
||||||
BarChart,
|
BarChart,
|
||||||
LineChart,
|
LineChart,
|
||||||
|
PieChart,
|
||||||
GridComponent,
|
GridComponent,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ interface DashboardTask {
|
|||||||
startedAt: string
|
startedAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoginDurationStat {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
duration: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RecentLoginUser {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
role: string
|
||||||
|
lastLogin: string
|
||||||
|
}
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const period = ref('7d')
|
const period = ref('7d')
|
||||||
|
|
||||||
@@ -185,6 +198,110 @@ const chartOption = computed<EChartsOption>(() => ({
|
|||||||
],
|
],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const operationChartOption = computed<EChartsOption>(() => ({
|
||||||
|
animationDuration: 500,
|
||||||
|
tooltip: { trigger: 'item' },
|
||||||
|
color: ['#4f46e5', '#10b981', '#f59e0b', '#3b82f6', '#ec4899'],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '操作分类',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '64%'],
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
avoidLabelOverlap: true,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 6,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'outside',
|
||||||
|
formatter: '{b}',
|
||||||
|
color: '#475569',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 16,
|
||||||
|
width: 70,
|
||||||
|
overflow: 'truncate',
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: { show: true, fontSize: 12, fontWeight: 'bold', color: '#1e293b' }
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: true,
|
||||||
|
length: 10,
|
||||||
|
length2: 8,
|
||||||
|
lineStyle: { color: '#94a3b8', width: 1 },
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ value: 1048, name: '模型训练' },
|
||||||
|
{ value: 735, name: '数据处理' },
|
||||||
|
{ value: 580, name: '模型评测' },
|
||||||
|
{ value: 484, name: '模型推理' },
|
||||||
|
{ value: 300, name: '系统设置' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const loginDurationStats: LoginDurationStat[] = [
|
||||||
|
{ id: 1, username: 'admin', duration: 124 },
|
||||||
|
{ id: 2, username: 'zhangsan', duration: 86 },
|
||||||
|
{ id: 3, username: 'lisi', duration: 42 },
|
||||||
|
{ id: 4, username: 'wangwu', duration: 18 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const loginDurationChartOption = computed<EChartsOption>(() => ({
|
||||||
|
animationDuration: 500,
|
||||||
|
grid: { top: 8, right: 40, bottom: 6, left: 8, containLabel: true },
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: { type: 'shadow' },
|
||||||
|
valueFormatter: (value) => `${value} 小时`,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'value',
|
||||||
|
max: Math.ceil(Math.max(...loginDurationStats.map((user) => user.duration)) / 10) * 10,
|
||||||
|
splitNumber: 4,
|
||||||
|
axisLabel: { color: '#94a3b8', fontSize: 11, formatter: '{value}h' },
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
splitLine: { lineStyle: { color: '#eef2f7' } },
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
inverse: true,
|
||||||
|
data: loginDurationStats.map((user) => user.username),
|
||||||
|
axisLabel: { color: '#475569', fontSize: 12 },
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '登录时长',
|
||||||
|
type: 'bar',
|
||||||
|
data: loginDurationStats.map((user) => user.duration),
|
||||||
|
barMaxWidth: 18,
|
||||||
|
barCategoryGap: '34%',
|
||||||
|
itemStyle: { color: '#4f46e5', borderRadius: [0, 4, 4, 0] },
|
||||||
|
label: { show: true, position: 'right', distance: 8, color: '#64748b', fontSize: 11, formatter: '{c} 小时' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
|
||||||
|
const recentLoginUsers: RecentLoginUser[] = [
|
||||||
|
{ id: 1, username: 'admin', role: '超级管理员', lastLogin: '10 分钟前' },
|
||||||
|
{ id: 2, username: 'zhangsan', role: '操作员', lastLogin: '2 小时前' },
|
||||||
|
{ id: 5, username: 'zhaoliu', role: '观察员', lastLogin: '5 小时前' },
|
||||||
|
{ id: 3, username: 'lisi', role: '操作员', lastLogin: '昨天 15:30' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const roleTagType: Record<string, 'danger' | 'primary' | 'info'> = {
|
||||||
|
'超级管理员': 'danger',
|
||||||
|
'操作员': 'primary',
|
||||||
|
'观察员': 'info',
|
||||||
|
}
|
||||||
|
|
||||||
function viewAllTasks() {
|
function viewAllTasks() {
|
||||||
router.push('/fine-tune')
|
router.push('/fine-tune')
|
||||||
}
|
}
|
||||||
@@ -262,6 +379,38 @@ function viewTask(task: DashboardTask) {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="user-stats-row">
|
||||||
|
<section class="stat-card" aria-labelledby="op-dist-title">
|
||||||
|
<h2 id="op-dist-title" class="section-title">用户操作分布</h2>
|
||||||
|
<div class="chart-container">
|
||||||
|
<VChart class="pie-chart" :option="operationChartOption" autoresize />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="stat-card" aria-labelledby="login-dur-title">
|
||||||
|
<h2 id="login-dur-title" class="section-title">登录时长排行 (本月)</h2>
|
||||||
|
<VChart class="duration-chart" :option="loginDurationChartOption" autoresize />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="stat-card" aria-labelledby="recent-login-title">
|
||||||
|
<h2 id="recent-login-title" class="section-title">最近登录用户</h2>
|
||||||
|
<div class="list-container">
|
||||||
|
<div v-for="user in recentLoginUsers" :key="user.id" class="list-item recent-user-item">
|
||||||
|
<div class="user-info">
|
||||||
|
<el-avatar :size="32" class="user-avatar" :style="{ backgroundColor: '#e2e8f0', color: '#475569' }">
|
||||||
|
{{ user.username.slice(0, 1).toUpperCase() }}
|
||||||
|
</el-avatar>
|
||||||
|
<div class="user-meta">
|
||||||
|
<span class="username">{{ user.username }}</span>
|
||||||
|
<span class="login-time">{{ user.lastLogin }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-tag :type="roleTagType[user.role]" size="small">{{ user.role }}</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section class="tasks-section" aria-labelledby="tasks-title">
|
<section class="tasks-section" aria-labelledby="tasks-title">
|
||||||
<div class="section-heading-row tasks-heading">
|
<div class="section-heading-row tasks-heading">
|
||||||
<h2 id="tasks-title" class="section-title">训练任务</h2>
|
<h2 id="tasks-title" class="section-title">训练任务</h2>
|
||||||
@@ -319,22 +468,102 @@ function viewTask(task: DashboardTask) {
|
|||||||
.dashboard-view {
|
.dashboard-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
min-height: 100%;
|
||||||
gap: 10px;
|
gap: 16px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
|
||||||
color: #1e293b;
|
color: #1e293b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overview-section,
|
.overview-section,
|
||||||
.chart-section,
|
.chart-section,
|
||||||
.service-section,
|
.service-section,
|
||||||
|
.stat-card,
|
||||||
.tasks-section {
|
.tasks-section {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-stats-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px 18px;
|
||||||
|
min-height: 284px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 224px;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 224px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 224px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-time {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-user-item {
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
@@ -429,8 +658,8 @@ function viewTask(task: DashboardTask) {
|
|||||||
.dashboard-middle {
|
.dashboard-middle {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.9fr) minmax(300px, 0.82fr);
|
grid-template-columns: minmax(0, 1.9fr) minmax(300px, 0.82fr);
|
||||||
flex: 1 1 auto;
|
flex: 0 0 auto;
|
||||||
gap: 10px;
|
gap: 16px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,8 +667,8 @@ function viewTask(task: DashboardTask) {
|
|||||||
.service-section {
|
.service-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 280px;
|
min-height: 360px;
|
||||||
padding: 12px 14px;
|
padding: 16px 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading-row {
|
.section-heading-row {
|
||||||
@@ -460,8 +689,8 @@ function viewTask(task: DashboardTask) {
|
|||||||
.training-chart {
|
.training-chart {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: 300px;
|
||||||
min-height: 230px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-table {
|
.service-table {
|
||||||
@@ -614,10 +843,6 @@ function viewTask(task: DashboardTask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 900px) and (min-width: 1181px) {
|
@media (max-height: 900px) and (min-width: 1181px) {
|
||||||
.dashboard-view {
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@@ -666,20 +891,6 @@ function viewTask(task: DashboardTask) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-middle {
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-section,
|
|
||||||
.service-section {
|
|
||||||
min-height: 240px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.training-chart {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-table {
|
.service-table {
|
||||||
grid-template-rows: 32px repeat(4, minmax(40px, 1fr));
|
grid-template-rows: 32px repeat(4, minmax(40px, 1fr));
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
@@ -741,6 +952,17 @@ function viewTask(task: DashboardTask) {
|
|||||||
.dashboard-middle {
|
.dashboard-middle {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-stats-row {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.user-stats-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
|||||||
Reference in New Issue
Block a user