feat:仪表盘界面修改以及中英文适配
This commit is contained in:
@@ -540,15 +540,23 @@ const local: any = {
|
|||||||
pay:'pay now'
|
pay:'pay now'
|
||||||
},
|
},
|
||||||
headerbanner:{
|
headerbanner:{
|
||||||
Remainingcredit:'Remain credit',
|
packageinfo:'Package',
|
||||||
|
packagename:'Package name',
|
||||||
|
price:'Price',
|
||||||
|
uplimit:'Up speed',
|
||||||
|
downlimit:'Down speed',
|
||||||
|
client:'Client',
|
||||||
|
accountinfor:'Account information',
|
||||||
|
Remainingcredit:'Remain balance',
|
||||||
monthphonebill:'Month bill',
|
monthphonebill:'Month bill',
|
||||||
Flowremaining:'Remain flowr',
|
Flowremaining:'Remain traffic',
|
||||||
monthflowr:'Month flowr',
|
monthflowr:'Traffic',
|
||||||
Trafficrate:'Traffic rate',
|
Trafficrate:'Traffic rate',
|
||||||
Currentspeed:'Current speed',
|
Currentspeed:'Current speed',
|
||||||
Used:'Used',
|
Used:'Used',
|
||||||
maxspeed:'Max',
|
maxspeed:'Max',
|
||||||
deviceCount: "Device",
|
deviceCount: "Device",
|
||||||
|
nolimit:'Unlimited',
|
||||||
},
|
},
|
||||||
userInfo:{
|
userInfo:{
|
||||||
kyc:'KYC certification',
|
kyc:'KYC certification',
|
||||||
|
|||||||
@@ -542,6 +542,13 @@ const local:any = {
|
|||||||
pay:'立即支付',
|
pay:'立即支付',
|
||||||
},
|
},
|
||||||
headerbanner:{
|
headerbanner:{
|
||||||
|
packageinfo:'套餐信息',
|
||||||
|
packagename:'套餐名称',
|
||||||
|
price:'套餐费用',
|
||||||
|
uplimit:'上行速率',
|
||||||
|
downlimit:'下行速率',
|
||||||
|
client:'设备数',
|
||||||
|
accountinfor:'账户信息',
|
||||||
Remainingcredit:'剩余话费',
|
Remainingcredit:'剩余话费',
|
||||||
monthphonebill:'本月话费',
|
monthphonebill:'本月话费',
|
||||||
Flowremaining:'剩余流量',
|
Flowremaining:'剩余流量',
|
||||||
@@ -551,6 +558,7 @@ const local:any = {
|
|||||||
Used:'已用',
|
Used:'已用',
|
||||||
maxspeed:'峰值',
|
maxspeed:'峰值',
|
||||||
deviceCount: "设备数",
|
deviceCount: "设备数",
|
||||||
|
nolimit:'无限制',
|
||||||
},
|
},
|
||||||
userInfo:{
|
userInfo:{
|
||||||
kyc:'KYC认证',
|
kyc:'KYC认证',
|
||||||
|
|||||||
11
src/typings/api.d.ts
vendored
11
src/typings/api.d.ts
vendored
@@ -453,8 +453,15 @@ declare namespace Api {
|
|||||||
traffic: number;
|
traffic: number;
|
||||||
trafficUsed: number;
|
trafficUsed: number;
|
||||||
trafficEnable: boolean;
|
trafficEnable: boolean;
|
||||||
activity: number;
|
rateLimitEnable: boolean;
|
||||||
error?: any; // 添加可选的 error 属性
|
upLimitEnable: boolean;
|
||||||
|
downLimitEnable: boolean;
|
||||||
|
upLimit: number;
|
||||||
|
downLimit: number;
|
||||||
|
packageName:string;
|
||||||
|
clientNumEnable:boolean;
|
||||||
|
price:string;
|
||||||
|
error?: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ interface GaugeDisplayData {
|
|||||||
displayValue?: string;
|
displayValue?: string;
|
||||||
subTitle?: string;
|
subTitle?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
speedLimits?: {
|
||||||
|
upLimit: string;
|
||||||
|
downLimit: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加速率单位转换函数
|
// 添加速率单位转换函数
|
||||||
@@ -74,7 +78,7 @@ const formatBalance = (balance: number | string): string => {
|
|||||||
return numBalance.toFixed(2);
|
return numBalance.toFixed(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 使用 ref 来存储基础数据
|
// 修改基础数据,移除速率仪表盘
|
||||||
const baseData = ref<GaugeDisplayData[]>([
|
const baseData = ref<GaugeDisplayData[]>([
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
@@ -92,16 +96,11 @@ const baseData = ref<GaugeDisplayData[]>([
|
|||||||
max: 1024,
|
max: 1024,
|
||||||
unit: 'B',
|
unit: 'B',
|
||||||
description: t('page.headerbanner.monthflowr'),
|
description: t('page.headerbanner.monthflowr'),
|
||||||
subTitle: t('page.headerbanner.Used') + ': 0B'
|
subTitle: t('page.headerbanner.Used') + ': 0B',
|
||||||
},
|
speedLimits: {
|
||||||
{
|
upLimit: '无限制',
|
||||||
id: 2,
|
downLimit: '无限制'
|
||||||
title: t('page.headerbanner.Trafficrate'),
|
}
|
||||||
value: 0,
|
|
||||||
max: 10,
|
|
||||||
unit: 'B/s',
|
|
||||||
description: t('page.headerbanner.Currentspeed'),
|
|
||||||
subTitle: t('page.headerbanner.maxspeed') + '0B/s'
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -179,9 +178,6 @@ const { domRef: gauge1Ref, updateOptions: updateGauge1 } = useEcharts(() => getG
|
|||||||
const { domRef: gauge2Ref, updateOptions: updateGauge2 } = useEcharts(() => getGaugeOptions(gaugeData.value[1]), {
|
const { domRef: gauge2Ref, updateOptions: updateGauge2 } = useEcharts(() => getGaugeOptions(gaugeData.value[1]), {
|
||||||
onRender: () => {}
|
onRender: () => {}
|
||||||
});
|
});
|
||||||
const { domRef: gauge3Ref, updateOptions: updateGauge3 } = useEcharts(() => getGaugeOptions(gaugeData.value[2]), {
|
|
||||||
onRender: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新单个图表的数据
|
// 更新单个图表的数据
|
||||||
const updateGaugeData = (opts: ECOption, data: GaugeDisplayData, progressRatio?: number) => {
|
const updateGaugeData = (opts: ECOption, data: GaugeDisplayData, progressRatio?: number) => {
|
||||||
@@ -254,126 +250,106 @@ const updateGaugeData = (opts: ECOption, data: GaugeDisplayData, progressRatio?:
|
|||||||
return newOpts;
|
return newOpts;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加峰值速率的 ref
|
// 修改速率格式化函数
|
||||||
const peakTrafficRate = ref(0);
|
const formatSpeedLimit = (speed: number): string => {
|
||||||
|
if (speed >= 1000000) {
|
||||||
// 添加检查是否可以上网的函数
|
return `${(speed / 1000000).toFixed(1)}Gbps`;
|
||||||
function checkAndTriggerAuth(dashboardData: any) {
|
} else if (speed >= 1000) {
|
||||||
// 检查是否有余额
|
return `${(speed / 1000).toFixed(1)}Mbps`;
|
||||||
const hasBalance = dashboardData.balance > 0;
|
|
||||||
// 检查是否不限制流量
|
|
||||||
const noTrafficLimit = !dashboardData.trafficEnable;
|
|
||||||
// 检查是否有剩余流量
|
|
||||||
const hasRemainingTraffic = dashboardData.trafficEnable &&
|
|
||||||
(dashboardData.traffic - dashboardData.trafficUsed) > 0;
|
|
||||||
|
|
||||||
// 如果满足任一条件,静默触发上网
|
|
||||||
if (hasBalance || noTrafficLimit || hasRemainingTraffic) {
|
|
||||||
clientAuth().catch(err => {
|
|
||||||
console.warn('Auto auth failed:', err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
return `${speed}Kbps`;
|
||||||
|
};
|
||||||
|
|
||||||
// 修改 mockDataUpdate 函数,在获取数据后添加检查
|
// 添加速率限制的响应式引用
|
||||||
|
const speedLimits = ref({
|
||||||
|
upLimit: t('page.headerbanner.nolimit'),
|
||||||
|
downLimit: t('page.headerbanner.nolimit')
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加套餐信息的响应式引用
|
||||||
|
const packageInfo = ref({
|
||||||
|
packageName: '',
|
||||||
|
price: '0.00'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 修改数据更新函数,添加套餐信息的更新
|
||||||
async function mockDataUpdate() {
|
async function mockDataUpdate() {
|
||||||
try {
|
try {
|
||||||
const response = await authStore.getDashboardData();
|
const response = await authStore.getDashboardData();
|
||||||
// 检查响应是否有效且不是错误响应
|
|
||||||
if (response && typeof response === 'object' && !response.error) {
|
if (response && typeof response === 'object' && !response.error) {
|
||||||
// 检查必要的字段是否存在
|
// 更新套餐信息
|
||||||
if (response.balance !== undefined) {
|
packageInfo.value = {
|
||||||
// 更新余额和设备数据
|
packageName: response.packageName || '暂无套餐',
|
||||||
const numBalance = Number(response.balance);
|
price: formatBalance(response.price || 0)
|
||||||
baseData.value[0] = {
|
};
|
||||||
...baseData.value[0],
|
|
||||||
value: numBalance,
|
|
||||||
max: Math.max(numBalance, 100),
|
|
||||||
displayValue: formatBalance(response.balance),
|
|
||||||
unit: '元',
|
|
||||||
subTitle: t('page.headerbanner.deviceCount') + `: ${response.clientNum || 0}台`
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理流量数据,使用默认值处理可能的空值
|
// 更新余额和设备数据
|
||||||
const totalTraffic = response.traffic || 0;
|
const numBalance = Number(response.balance);
|
||||||
const usedTraffic = response.trafficUsed || 0;
|
baseData.value[0] = {
|
||||||
const remainingTraffic = Math.max(0, totalTraffic - usedTraffic);
|
...baseData.value[0],
|
||||||
const progressRatio = totalTraffic ? Math.min(1, Math.max(0, remainingTraffic / totalTraffic)) : 0;
|
value: numBalance,
|
||||||
|
max: Math.max(numBalance, 100),
|
||||||
|
displayValue: formatBalance(response.balance),
|
||||||
|
unit: '元',
|
||||||
|
subTitle: t('page.headerbanner.deviceCount') + `: ${response.clientNumEnable ? (response.clientNum || 0) + '台' : t('page.headerbanner.nolimit')}`
|
||||||
|
};
|
||||||
|
|
||||||
// 格式化流量显示
|
// 更新流量数据
|
||||||
const formattedTotal = formatTraffic(totalTraffic);
|
const totalTraffic = response.trafficEnable ? (response.traffic || 0) : 0;
|
||||||
const formattedUsed = formatTraffic(usedTraffic);
|
const usedTraffic = response.trafficEnable ? (response.trafficUsed || 0) : 0;
|
||||||
const formattedRemaining = formatTraffic(remainingTraffic);
|
const remainingTraffic = Math.max(0, totalTraffic - usedTraffic);
|
||||||
|
const progressRatio = totalTraffic ? Math.min(1, Math.max(0, remainingTraffic / totalTraffic)) : 0;
|
||||||
|
|
||||||
// 更新流量数据显示
|
// 格式化流量显示
|
||||||
baseData.value[1] = {
|
const formattedTotal = response.trafficEnable ? formatTraffic(totalTraffic) : { value: 0, unit: '' };
|
||||||
...baseData.value[1],
|
const formattedUsed = response.trafficEnable ? formatTraffic(usedTraffic) : { value: 0, unit: '' };
|
||||||
value: remainingTraffic,
|
const formattedRemaining = response.trafficEnable ? formatTraffic(remainingTraffic) : { value: 0, unit: '' };
|
||||||
max: totalTraffic,
|
|
||||||
displayValue: `${formattedRemaining.value}${formattedRemaining.unit}`,
|
|
||||||
unit: '',
|
|
||||||
description: `${t('page.headerbanner.monthflowr')} (${formattedTotal.value}${formattedTotal.unit})`,
|
|
||||||
subTitle: t('page.headerbanner.Used') + `: ${formattedUsed.value}${formattedUsed.unit}`
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取当前速率
|
// 更新流量数据显示
|
||||||
const currentActivity = Number(response.activity ?? 0);
|
baseData.value[1] = {
|
||||||
|
...baseData.value[1],
|
||||||
// 更新峰值速率
|
value: remainingTraffic,
|
||||||
if (currentActivity > peakTrafficRate.value) {
|
max: totalTraffic,
|
||||||
peakTrafficRate.value = currentActivity;
|
displayValue: response.trafficEnable ? `${formattedRemaining.value}${formattedRemaining.unit}` : t('page.headerbanner.nolimit'),
|
||||||
|
unit: '',
|
||||||
|
description: response.trafficEnable
|
||||||
|
? `${t('page.headerbanner.monthflowr')} (${formattedTotal.value}${formattedTotal.unit})`
|
||||||
|
: `${t('page.headerbanner.monthflowr')} (${t('page.headerbanner.nolimit')})`,
|
||||||
|
subTitle: response.trafficEnable
|
||||||
|
? t('page.headerbanner.Used') + `: ${formattedUsed.value}${formattedUsed.unit}`
|
||||||
|
: t('page.headerbanner.Used') + `: 0B`,
|
||||||
|
speedLimits: {
|
||||||
|
upLimit: speedLimits.value.upLimit,
|
||||||
|
downLimit: speedLimits.value.downLimit
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 格式化当前速度和峰值速度
|
// 更新速率限制显示
|
||||||
const currentSpeed = formatSpeed(currentActivity);
|
if (response.rateLimitEnable) {
|
||||||
const peakSpeed = formatSpeed(peakTrafficRate.value);
|
speedLimits.value = {
|
||||||
|
upLimit: response.upLimitEnable ? formatSpeedLimit(response.upLimit) : t('page.headerbanner.nolimit'),
|
||||||
// 设置速率的最大值
|
downLimit: response.downLimitEnable ? formatSpeedLimit(response.downLimit) : t('page.headerbanner.nolimit')
|
||||||
const minMax = 1024; // 1KB/s 作为最小最大值
|
|
||||||
const dynamicMax = Math.max(
|
|
||||||
currentSpeed.value * 1.5,
|
|
||||||
peakSpeed.value,
|
|
||||||
minMax
|
|
||||||
);
|
|
||||||
|
|
||||||
// 更新速率数据
|
|
||||||
baseData.value[2] = {
|
|
||||||
...baseData.value[2],
|
|
||||||
value: currentSpeed.value,
|
|
||||||
unit: currentSpeed.unit,
|
|
||||||
max: dynamicMax,
|
|
||||||
subTitle: t('page.headerbanner.maxspeed') + `: ${peakSpeed.value}${peakSpeed.unit}`
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 静默执行自动上网检查
|
|
||||||
try {
|
|
||||||
await checkAndTriggerAuth(response);
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Auto auth check failed:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新图表
|
|
||||||
updateGauge1(opts => updateGaugeData(opts, baseData.value[0]));
|
|
||||||
updateGauge2(opts => {
|
|
||||||
const newOpts = updateGaugeData(opts, baseData.value[1], progressRatio);
|
|
||||||
return {
|
|
||||||
...newOpts,
|
|
||||||
animation: true,
|
|
||||||
animationDuration: 1000,
|
|
||||||
animationEasing: 'cubicInOut'
|
|
||||||
};
|
|
||||||
});
|
|
||||||
updateGauge3(opts => updateGaugeData(opts, baseData.value[2]));
|
|
||||||
} else {
|
} else {
|
||||||
// 静默处理无效数据
|
speedLimits.value = {
|
||||||
console.warn('Invalid dashboard data structure:', response);
|
upLimit: t('page.headerbanner.nolimit'),
|
||||||
|
downLimit: t('page.headerbanner.nolimit')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// 静默处理无效响应
|
// 更新图表
|
||||||
console.warn('Invalid response:', response);
|
updateGauge1(opts => updateGaugeData(opts, baseData.value[0]));
|
||||||
|
updateGauge2(opts => {
|
||||||
|
const newOpts = updateGaugeData(opts, baseData.value[1], progressRatio);
|
||||||
|
return {
|
||||||
|
...newOpts,
|
||||||
|
animation: true,
|
||||||
|
animationDuration: 1000,
|
||||||
|
animationEasing: 'cubicInOut'
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 静默处理所有错误
|
|
||||||
console.warn('Dashboard update failed:', error);
|
console.warn('Dashboard update failed:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,7 +371,6 @@ onUnmounted(() => {
|
|||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
timer = null;
|
timer = null;
|
||||||
}
|
}
|
||||||
peakTrafficRate.value = 0;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听语言变化
|
// 监听语言变化
|
||||||
@@ -418,27 +393,87 @@ const updateDashboard = async () => {
|
|||||||
defineExpose({
|
defineExpose({
|
||||||
updateDashboard
|
updateDashboard
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 修改流量获取函数
|
||||||
|
const getTrafficTotal = (description?: string, trafficEnable?: boolean): string => {
|
||||||
|
if (!trafficEnable) return t('page.headerbanner.nolimit');
|
||||||
|
if (!description) return '0B';
|
||||||
|
const matches = description.match(/\((.*?)\)/);
|
||||||
|
return matches ? matches[1] : '0B';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修改设备数量获取函数
|
||||||
|
const getDeviceCount = (subTitle?: string, clientNumEnable?: boolean): string => {
|
||||||
|
if (!clientNumEnable) return t('page.headerbanner.nolimit');
|
||||||
|
if (!subTitle) return '0';
|
||||||
|
const parts = subTitle.split(': ');
|
||||||
|
return parts.length > 1 ? parts[1] : '0';
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ACard :bordered="false" class="card-wrapper">
|
<ACard :bordered="false" class="card-wrapper">
|
||||||
<ARow :gutter="[4, 4]" class="justify-around gauge-row">
|
<div class="dashboard-layout">
|
||||||
<ACol :span="8" v-for="item in gaugeData" :key="item.id">
|
<!-- 左侧:仪表盘和流量信息 -->
|
||||||
|
<div class="left-section">
|
||||||
<div class="gauge-container">
|
<div class="gauge-container">
|
||||||
<div class="gauge-chart" :ref="el => {
|
<div class="gauge-chart" ref="gauge2Ref"></div>
|
||||||
if (item.id === 0) gauge1Ref = el as HTMLElement
|
|
||||||
else if (item.id === 1) gauge2Ref = el as HTMLElement
|
|
||||||
else if (item.id === 2) gauge3Ref = el as HTMLElement
|
|
||||||
}"></div>
|
|
||||||
<div class="gauge-info">
|
<div class="gauge-info">
|
||||||
<div class="gauge-title">{{ item.title }}</div>
|
<div class="gauge-title">{{ baseData[1].title }}</div>
|
||||||
<div class="gauge-value">{{ item.displayValue ? `${item.displayValue}${item.unit}` : `${item.value}${item.unit}` }}</div>
|
<div class="gauge-value">{{ baseData[1].displayValue }}</div>
|
||||||
<div class="gauge-desc">{{ item.description }}</div>
|
|
||||||
<div class="sub-title">{{ item.subTitle }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ACol>
|
</div>
|
||||||
</ARow>
|
|
||||||
|
<!-- 右侧:分组信息显示 -->
|
||||||
|
<div class="right-section">
|
||||||
|
<!-- 套餐信息 -->
|
||||||
|
<div class="info-section">
|
||||||
|
<div class="section-title">{{t('page.headerbanner.packageinfo')}}</div>
|
||||||
|
<div class="info-group">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.packagename') }}</span>
|
||||||
|
<span class="info-value">{{ packageInfo.packageName || '暂无套餐' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.price') }}</span>
|
||||||
|
<span class="info-value">¥{{ packageInfo.price || '0.00' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.monthflowr') }}</span>
|
||||||
|
<span class="info-value">{{ getTrafficTotal(baseData[1].description, baseData[1].speedLimits?.upLimit !== t('page.headerbanner.nolimit')) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.Used') }}</span>
|
||||||
|
<span class="info-value">{{ getTrafficTotal(baseData[1].description, baseData[1].speedLimits?.downLimit !== t('page.headerbanner.nolimit')) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.uplimit') }}</span>
|
||||||
|
<span class="info-value">{{ speedLimits.upLimit }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.downlimit') }}</span>
|
||||||
|
<span class="info-value">{{ speedLimits.downLimit }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 账户信息 -->
|
||||||
|
<div class="info-section">
|
||||||
|
<div class="section-title">{{ t('page.headerbanner.accountinfor') }}</div>
|
||||||
|
<div class="info-group">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.Remainingcredit') }}</span>
|
||||||
|
<span class="info-value">¥{{ baseData[0].displayValue }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ t('page.headerbanner.client') }}</span>
|
||||||
|
<span class="info-value">{{ getDeviceCount(baseData[0].subTitle, baseData[0].speedLimits?.upLimit !== t('page.headerbanner.nolimit')) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</ACard>
|
</ACard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -446,141 +481,224 @@ defineExpose({
|
|||||||
.card-wrapper {
|
.card-wrapper {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
background: linear-gradient(180deg, #4284f5 0%, #0c47a7 100%);
|
background: linear-gradient(180deg, #4284f5 0%, #0c47a7 100%);
|
||||||
padding: 20px 16px;
|
padding: 16px 12px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-layout {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧部分样式 */
|
||||||
|
.left-section {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-container {
|
.gauge-container {
|
||||||
height: 220px;
|
text-align: center;
|
||||||
min-height: 140px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: -20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-chart {
|
.gauge-chart {
|
||||||
flex: 1;
|
width: 200px;
|
||||||
min-height: 120px;
|
height: 200px;
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.echarts-container) {
|
|
||||||
position: absolute !important;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-info {
|
.gauge-info {
|
||||||
padding: 4px 0;
|
margin-top: -16px;
|
||||||
text-align: center;
|
|
||||||
color: #fff;
|
|
||||||
margin-top: -20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-value {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-title {
|
.gauge-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-title {
|
|
||||||
color: rgba(255, 255, 255, 0.85);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧部分样式 */
|
||||||
|
.right-section {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding-left: 6px;
|
||||||
|
border-left: 3px solid #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-desc {
|
.info-group {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item:not(:last-child) {
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 在小屏幕下调整样式 */
|
.info-value {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端适配 */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.card-wrapper {
|
.card-wrapper {
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-layout {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-section {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 8px 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.arco-row) {
|
.section-title {
|
||||||
margin: 0 !important;
|
font-size: 14px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.arco-col) {
|
.info-group {
|
||||||
padding: 0 !important;
|
padding: 6px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-section {
|
||||||
|
width: 140px;
|
||||||
|
min-height: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-container {
|
.gauge-container {
|
||||||
height: 160px;
|
width: 140px;
|
||||||
min-height: 90px;
|
margin-left: -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-chart {
|
.gauge-chart {
|
||||||
min-height: 70px;
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-info {
|
.gauge-info {
|
||||||
padding: 2px 0;
|
margin-top: -16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-value {
|
.gauge-value {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-title {
|
.info-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin: 2px 0;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-title {
|
.info-value {
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
}
|
word-break: break-all;
|
||||||
|
|
||||||
.gauge-desc {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 调整仪表盘在小屏幕下的置 */
|
|
||||||
:deep(.echarts-container) {
|
|
||||||
top: 0;
|
|
||||||
transform: scale(0.9);
|
|
||||||
transform-origin: center 45%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-info {
|
|
||||||
margin-top: -15px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 在超小屏幕下进一步优化 */
|
/* 超小屏幕优化 */
|
||||||
@media screen and (max-width: 375px) {
|
@media screen and (max-width: 375px) {
|
||||||
.card-wrapper {
|
.card-wrapper {
|
||||||
padding: 4px 2px;
|
padding: 10px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-layout {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-section {
|
||||||
|
width: 120px;
|
||||||
|
min-height: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-container {
|
.gauge-container {
|
||||||
height: 140px;
|
width: 120px;
|
||||||
min-height: 80px;
|
margin-left: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-chart {
|
.gauge-chart {
|
||||||
min-height: 60px;
|
width: 120px;
|
||||||
}
|
height: 120px;
|
||||||
|
|
||||||
:deep(.echarts-container) {
|
|
||||||
top: 0;
|
|
||||||
transform: scale(0.85);
|
|
||||||
transform-origin: center 40%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-info {
|
.gauge-info {
|
||||||
margin-top: -10px;
|
margin-top: -12px;
|
||||||
padding: 1px 0;
|
}
|
||||||
|
|
||||||
|
.gauge-value {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-group {
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
padding: 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user