<template>
|
<div class="app-container home">
|
<div class="p-5">
|
<div style="display: flex" class="gap-4">
|
|
<el-card style="width: 100%;flex: 1;" class="mb-4">
|
|
<div style="display: flex">
|
<div>
|
<el-statistic :value="batchScore" :suffix="'分'" :precision="1" :value-style="{fontSize: '30px',fontWeight: 'bold'}">
|
<template #title>
|
<div style="display: inline-flex; align-items: center;font-size: 18px">
|
综合质量评分
|
<el-tooltip effect="dark" content="" placement="top">
|
<el-icon style="margin-left: 4px" :size="12">
|
<Warning />
|
</el-icon>
|
</el-tooltip>
|
</div>
|
</template>
|
</el-statistic>
|
<div class="statistic-footer">
|
<div class="footer-item">
|
<span>较昨天</span>
|
<span :class="{ 'green': batchScore > 9.4, 'red': batchScore <= 9.4 }">
|
<el-icon v-if="batchScore > 9.4">
|
<CaretTop />
|
</el-icon>
|
<el-icon v-else>
|
<CaretBottom />
|
</el-icon>
|
</span>
|
</div>
|
</div>
|
</div>
|
<div style="flex: 1;display: flex;justify-content: center;align-items: center">
|
<el-image style="width: 60px; height: 60px" :src="q1" fit="contain" />
|
</div>
|
</div>
|
|
|
</el-card>
|
<el-card style="flex: 1" class="mb-4">
|
|
|
<div style="display: flex">
|
<div>
|
<el-statistic :value="qualityRate" :suffix="'%'" :value-style="{fontSize: '30px',fontWeight: 'bold'}">
|
<template #title>
|
<div style="display: inline-flex; align-items: center;font-size: 18px">
|
良品率
|
<el-tooltip effect="dark" content="" placement="top">
|
<el-icon style="margin-left: 4px" :size="12">
|
<Warning />
|
</el-icon>
|
</el-tooltip>
|
</div>
|
</template>
|
</el-statistic>
|
<div class="statistic-footer">
|
<div class="footer-item">
|
<span>较昨天</span>
|
<span :class="{ 'green': qualityRate >= 95, 'red': qualityRate < 95 }">
|
<el-icon v-if="qualityRate >= 95">
|
<CaretTop />
|
</el-icon>
|
<el-icon v-else>
|
<CaretBottom />
|
</el-icon>
|
</span>
|
</div>
|
</div>
|
</div>
|
<div style="flex: 1;display: flex;justify-content: center;align-items: center">
|
<el-image style="width: 60px; height: 60px" :src="q2" fit="contain" />
|
</div>
|
</div>
|
|
|
</el-card>
|
<el-card style="flex: 1" class="mb-4">
|
|
|
<div style="display: flex">
|
<div>
|
<el-statistic :value="checkNum" :suffix="'pcs'" :value-style="{fontSize: '30px',fontWeight: 'bold'}">
|
<template #title>
|
<div style="display: inline-flex; align-items: center;font-size: 18px">
|
检测数量
|
<el-tooltip effect="dark" content="" placement="top">
|
<el-icon style="margin-left: 4px" :size="12">
|
<Warning />
|
</el-icon>
|
</el-tooltip>
|
</div>
|
</template>
|
</el-statistic>
|
<div class="statistic-footer">
|
<div class="footer-item">
|
<span>较昨天</span>
|
<span :class="{ 'green': batchScore > 1, 'red': batchScore <= 1 }">
|
<el-icon v-if="batchScore > 1">
|
<CaretTop />
|
</el-icon>
|
<el-icon v-else>
|
<CaretBottom />
|
</el-icon>
|
</span>
|
</div>
|
</div>
|
</div>
|
<div style="flex: 1;display: flex;justify-content: center;align-items: center">
|
<el-image style="width: 60px; height: 60px" :src="q3" fit="contain" />
|
</div>
|
</div>
|
|
|
</el-card>
|
<el-card style="flex: 1" class="mb-4">
|
|
|
<div style="display: flex">
|
<div>
|
<el-statistic :value="ngCountNum" :suffix="'pcs'" :value-style="{fontSize: '30px',fontWeight: 'bold'}">
|
<template #title>
|
<div style="display: inline-flex; align-items: center;font-size: 18px">
|
异常数量
|
<el-tooltip effect="dark" content="" placement="top">
|
<el-icon style="margin-left: 4px" :size="12">
|
<Warning />
|
</el-icon>
|
</el-tooltip>
|
</div>
|
</template>
|
</el-statistic>
|
<div class="statistic-footer">
|
<div class="footer-item">
|
<span>较昨天</span>
|
<span :class="{ 'green': ngCountNum < 50, 'red': ngCountNum >= 50 }">
|
<el-icon v-if="ngCountNum >= 50">
|
<CaretTop />
|
</el-icon>
|
<el-icon v-else>
|
<CaretBottom />
|
</el-icon>
|
</span>
|
|
<span style="margin-left: 10px">预测异常率:</span>
|
<span class="red" v-if="qualityRate > 80"> {{(((100-qualityRate)/ 3)+0.1).toFixed(1) }}% </span>
|
<span v-else > 3.7%</span>
|
</div>
|
</div>
|
</div>
|
<div style="flex: 1;display: flex;justify-content: center;align-items: center">
|
<el-image style="width: 60px; height: 60px" :src="q4" fit="contain" />
|
</div>
|
</div>
|
</el-card>
|
</div>
|
<!-- 设备健康状态可视化 -->
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
<el-card shadow="hover" class="h-420" >
|
<template #header>
|
<div class="card-header">
|
<span>健康度评分</span>
|
</div>
|
</template>
|
<div ref="healthChartRef" class="h-400 w-full"></div>
|
</el-card>
|
|
<el-card shadow="hover" class="h-420">
|
<template #header>
|
<div class="card-header">
|
<span>质量预警信息</span>
|
</div>
|
</template>
|
<!-- <div class="grid grid-cols-2 gap-2">-->
|
<!-- <div class="p-2 bg-blue-50 rounded">-->
|
<!-- <p class="text-sm text-gray-600">齐纳保护电压(黑)</p>-->
|
<!-- <p class="text-xl">39.00 V ≤ result ≤ 49.00 V</p>-->
|
<!-- </div>-->
|
<!-- <div class="p-2 bg-yellow-50 rounded">-->
|
<!-- <p class="text-sm text-gray-600">距离</p>-->
|
<!-- <p class="text-xl">4.75 mm ≤ result ≤ 5.15 mm</p>-->
|
<!-- </div>-->
|
<!-- <div class="p-2 bg-red-50 rounded">-->
|
<!-- <p class="text-sm text-gray-600">压降(黑)1</p>-->
|
<!-- <p class="text-xl">0.30 V ≤ result ≤ 3.00 V</p>-->
|
<!-- </div>-->
|
<!-- <div class="p-2 bg-green-50 rounded">-->
|
<!-- <p class="text-sm text-gray-600">回差</p>-->
|
<!-- <p class="text-xl">0.05 mm ≤ result ≤ 1.00 mm</p>-->
|
<!-- </div>-->
|
<!-- <div class="p-2 bg-blue-50 rounded">-->
|
<!-- <p class="text-sm text-gray-600">漏电流(黑)1</p>-->
|
<!-- <p class="text-xl">result ≤ 800.00 uA</p>-->
|
<!-- </div>-->
|
<!-- <div class="p-2 bg-yellow-50 rounded">-->
|
<!-- <p class="text-sm text-gray-600"></p>-->
|
<!-- <p class="text-xl font-bold"></p>-->
|
<!-- </div>-->
|
<!-- </div>-->
|
|
<el-table size="large" :data="warnBatchList" style="width: 100%" :border="true" stripe>
|
<el-table-column prop="batchTime" label="预警时间" />
|
<el-table-column prop="batchCode" label="批次号" width="140" />
|
<el-table-column prop="gw" label="设备/工位" />
|
<el-table-column prop="yc" label="异常类型" />
|
<el-table-column prop="cd" label="严重程度">
|
<template #default="{ row }">
|
<el-tag :type="getStatusTagType(row.cd)">{{ row.cd }}</el-tag>
|
</template>
|
</el-table-column>
|
|
|
</el-table>
|
</el-card>
|
</div>
|
|
|
<el-card shadow="hover" class="mb-6">
|
<template #header>
|
<div class="card-header">
|
<span>检测批次</span>
|
<el-date-picker
|
v-model="batchDate"
|
type="date"
|
placeholder="选择日期"
|
:disabled-date="disabledDate"
|
:shortcuts="shortcuts"
|
@change="changeBatchDate"
|
/>
|
</div>
|
</template>
|
<div class="grid grid-cols-1 gap-4">
|
<!-- 这里将放置预警表格 -->
|
<el-table v-loading="loading" :data="batchList" style="width: 100%" :border="true" stripe>
|
<el-table-column prop="batchCode" label="批次号" width="180" />
|
<el-table-column prop="prodModel" label="产品型号" width="180" />
|
<el-table-column prop="num" label="检测数量" width="120" />
|
<el-table-column prop="okNum" label="良品数量" width="120" />
|
<el-table-column prop="ngNum" label="不良数量" width="120" />
|
<el-table-column prop="lpv" label="良品率" width="120">
|
<template #default="{ row }">
|
<el-tag :type="goodProductsRate(row) >= 95 ? 'success' : 'warning'"> {{ goodProductsRate(row) }} % </el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="whjy" label="维护建议">
|
<template #default="{ row }">
|
<el-tag :type="goodProductsRate(row) >= 95 ? 'success' : 'warning'">
|
{{ goodProductsRate(row) >= 95 ? '良品率指标已优异,请关注设备综合效率' : '预警,检查工艺参数是否符合标准,优化关键参数' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="180">
|
<template #default="{ row }">
|
<el-button link type="primary" @click="handleDetail(row)">详情</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
</el-card>
|
|
|
|
|
|
<!-- 备件信息 -->
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
<!-- 批次合格率趋势 -->
|
<el-card shadow="hover" class="h-420">
|
<template #header>
|
<div class="card-header">
|
<span>批次合格率趋势</span>
|
</div>
|
</template>
|
<div ref="batchRateTrendRef" class="h-400 w-full"></div>
|
</el-card>
|
|
|
<el-card shadow="hover" class="h-420">
|
<template #header>
|
<div class="card-header">
|
<span>近一周NG项TOP5</span>
|
</div>
|
</template>
|
<div ref="ngChartRef" class="h-400 w-full"></div>
|
</el-card>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup name="Index" lang="ts">
|
import { onMounted, ref } from 'vue';
|
import { useRouter } from 'vue-router';
|
import { ElMessage } from 'element-plus';
|
import * as echarts from 'echarts';
|
|
import q1 from '@/assets/images/quailty/q1.png';
|
import q2 from '@/assets/images/quailty/q2.png';
|
import q3 from '@/assets/images/quailty/q3.png';
|
import q4 from '@/assets/images/quailty/q4.png';
|
|
import { queryQualityHealth, queryBatchList,queryPwbatchList ,queryNgrank} from '@/api/qms/index';
|
import { BatchVO } from '@/api/qms/batch/types';
|
import { listBatch } from '@/api/qms/batch';
|
|
const batchScore = ref(0);
|
const qualityRate = ref(0);
|
const checkNum = ref(0);
|
const ngCountNum = ref(0);
|
|
const batchDate = ref('');
|
|
const healthChartRef = ref<HTMLElement | null>(null);
|
const batchRateTrendRef = ref<HTMLElement | null>(null);
|
const ngChartRef = ref<HTMLElement | null>(null);
|
const healthChart = ref();
|
const batchRateChart = ref();
|
const ngChart = ref();
|
const router = useRouter();
|
|
const loading = ref(false);
|
const batchList = ref<BatchVO[]>([]);
|
const warnBatchList = ref<any>([]);
|
|
|
const gwList = [
|
"微机电系统蚀刻机",
|
"晶圆键合机",
|
"传感器校准站",
|
"薄膜沉积设备",
|
"光刻机",
|
"离子注入机",
|
"化学机械抛光设备",
|
"热处理炉",
|
"封装测试站",
|
"激光修调设备",
|
"电性能测试台",
|
"光学检测仪",
|
"真空镀膜机",
|
"超声波清洗机",
|
"X射线检测仪"
|
]
|
const ycList = [
|
"蚀刻深度偏差",
|
"键合压力异常",
|
"校准数据偏移",
|
"沉积速率波动",
|
"光刻对准偏差",
|
"离子注入剂量异常",
|
"抛光厚度不均",
|
"温度稳定性异常",
|
"封装密封性不良",
|
"电阻值偏差",
|
"灵敏度异常",
|
"响应时间超限",
|
"信号漂移异常",
|
"线性度偏差",
|
"零点漂移异常",
|
"过载恢复异常",
|
"绝缘电阻不足",
|
"介质耐压不合格",
|
"频率响应异常",
|
"信噪比不达标"
|
];
|
|
const cdList = [
|
"高", "高", "中", "中", "高",
|
"中", "中", "高", "高", "中",
|
"高", "中", "中", "中", "低",
|
"中", "高", "高", "中", "低"
|
];
|
const getStatusTagType = (status: string) => {
|
switch (status) {
|
case '高': return 'danger';
|
case '中': return 'warning';
|
case '低': return 'info';
|
}
|
};
|
|
function changeBatchDate(date: Date){
|
getBatch(date ? date : new Date())
|
|
}
|
async function queryWarnBatchList(){
|
const queryParams = {
|
pageNum: 1,
|
pageSize: 6
|
};
|
const res:any = await listBatch(queryParams);
|
|
if(res && res.rows){
|
warnBatchList.value = [];
|
res.rows.forEach((item,index) => {
|
item.gw = gwList[index];
|
item.yc = ycList[index];
|
item.cd = cdList[index];
|
item.batchTime = item.batchTime.substring(0,10);
|
warnBatchList.value.push(item);
|
})
|
}
|
|
}
|
|
const shortcuts = [
|
{
|
text: '今天',
|
value: new Date(),
|
},
|
{
|
text: '昨天',
|
value: () => {
|
const date = new Date()
|
date.setTime(date.getTime() - 3600 * 1000 * 24)
|
return date
|
},
|
},
|
{
|
text: '一周前',
|
value: () => {
|
const date = new Date()
|
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
|
return date
|
},
|
},
|
]
|
|
const disabledDate = (time: Date) => {
|
return time.getTime() > Date.now()
|
}
|
|
|
|
|
|
|
const goodProductsRate = (row) => {
|
if (row.num > 0 && row.okNum > 0) {
|
return Math.round((row.okNum / row.num) * 100);
|
}
|
};
|
|
|
|
|
|
|
|
|
|
|
const generateWorkOrder = (record) => {
|
// 工单生成逻辑
|
ElMessage.success(`已为 ${record.name} 生成工单`);
|
};
|
|
const handleDetail = (record) => {
|
router.push({ path: '/quality/detail', query: { id: record.id } });
|
};
|
|
const initHealthChart = () => {
|
if (healthChartRef.value) {
|
healthChart.value = echarts.init(healthChartRef.value);
|
const option = {
|
tooltip: {
|
trigger: 'axis',
|
axisPointer: {
|
type: 'shadow'
|
}
|
},
|
xAxis: {
|
type: 'category',
|
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
},
|
yAxis: {
|
type: 'value',
|
name: '得分',
|
minInterval: 1
|
},
|
series: [
|
{
|
name: '质量数据',
|
type: 'bar',
|
barWidth: '50%',
|
data: [
|
{ value: 9.8, itemStyle: { color: '#52c41a' } },
|
{ value: 9.9, itemStyle: { color: '#52c41a' } },
|
{ value: 9.7, itemStyle: { color: '#52c41a' } },
|
{ value: 9.8, itemStyle: { color: '#52c41a' } },
|
{ value: 9.9, itemStyle: { color: '#52c41a' } },
|
{ value: 9.8, itemStyle: { color: '#52c41a' } },
|
{ value: 9.9, itemStyle: { color: '#52c41a' } },
|
{ value: 9.8, itemStyle: { color: '#52c41a' } },
|
{ value: 9.8, itemStyle: { color: '#52c41a' } }
|
],
|
label: {
|
show: true,
|
position: 'top',
|
formatter: '{c}分'
|
}
|
}
|
]
|
};
|
healthChart.value.setOption(option);
|
|
window.addEventListener('resize', () => {
|
healthChart.value.resize();
|
});
|
}
|
};
|
|
const initBatchRateChart = () => {
|
if (batchRateTrendRef.value) {
|
batchRateChart.value = echarts.init(batchRateTrendRef.value);
|
const option = {
|
color: ['#FFBF00'],
|
tooltip: {
|
trigger: 'axis',
|
axisPointer: {
|
type: 'cross',
|
label: {
|
backgroundColor: '#6a7985'
|
}
|
}
|
},
|
|
xAxis: [
|
{
|
splitLine: { show: false },
|
type: 'category',
|
boundaryGap: false,
|
data: ['08-01', '08-02', '08-03', '08-04', '08-05', '08-06', '08-07']
|
}
|
],
|
yAxis: [
|
{
|
splitLine: { show: false },
|
type: 'value',
|
name: '检测合格率',
|
min: 80, // 设置最小值为90
|
max: 100, // 设置最大值为100
|
interval: 5, // 设置刻度间隔为1
|
axisLabel: {
|
formatter: '{value}%' // 添加百分比符号
|
}
|
}
|
],
|
series: [
|
{
|
name: '',
|
type: 'line',
|
stack: 'Total',
|
smooth: true,
|
lineStyle: {
|
width: 0
|
},
|
showSymbol: false,
|
areaStyle: {
|
opacity: 0.8,
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{
|
offset: 0,
|
color: 'rgb(128, 255, 165)'
|
},
|
{
|
offset: 1,
|
color: 'rgb(1, 191, 236)'
|
}
|
])
|
},
|
emphasis: {
|
focus: 'series'
|
},
|
data: [99, 98, 95, 99, 92, 98, 96],
|
|
}
|
]
|
};
|
|
option && batchRateChart.value.setOption(option);
|
|
|
window.addEventListener('resize', () => {
|
batchRateChart.value.resize();
|
});
|
}
|
};
|
|
const initNgChart = () => {
|
if (ngChartRef.value) {
|
ngChart.value = echarts.init(ngChartRef.value);
|
const option = {
|
|
tooltip: {
|
trigger: 'axis',
|
axisPointer: {
|
type: 'shadow'
|
}
|
},
|
grid: {
|
left: '120px', // 增加左侧边距,为标签留出更多空间
|
top: '0px',
|
},
|
xAxis: {
|
type: 'value',
|
boundaryGap: [0, 0.01],
|
splitLine: { show: false }
|
},
|
yAxis: {
|
splitLine: { show: false },
|
type: 'category',
|
data: [
|
'静态消耗电流2',
|
'回差',
|
'短路保护消耗电流(黑)2',
|
'动态消耗电流2',
|
'距离',
|
'压降(黑)2'
|
]
|
},
|
series: [
|
{
|
name: '',
|
type: 'bar',
|
barWidth: '60%',
|
data: [100, 90, 80, 70, 60, 10],
|
itemStyle: {
|
color: '#faad14' // 修改柱状图颜色
|
},
|
label: {
|
show: true,
|
position: 'right',
|
formatter: '{c}次'
|
}
|
}
|
]
|
};
|
|
option && ngChart.value.setOption(option);
|
|
|
|
window.addEventListener('resize', () => {
|
ngChart.value.resize();
|
});
|
}
|
};
|
|
const getHealth = async () => {
|
const res: any = await queryQualityHealth();
|
if (res && res.yAxis) {
|
const xAxisData = [];
|
const minValue = res.yAxis.reduce((min, item) => {
|
return min === null ? item.value : Math.min(min, item.value);
|
}, null);
|
res.yAxis.forEach((item, index) => {
|
if (item.value >= 9.5) {
|
item.itemStyle = { color: '#52c41a' };
|
} else if (item.value < 9.5 && item.value >= 9.0) {
|
item.itemStyle = { color: '#faad14' };
|
} else {
|
item.itemStyle = { color: '#f5222d' };
|
}
|
if (item.value == minValue) {
|
item.itemStyle = { color: '#faad14' };
|
}
|
xAxisData.push(item);
|
});
|
updateHealthData(res.xAxis, xAxisData);
|
}
|
};
|
|
const getBatch = async (date = new Date()) => {
|
loading.value = true;
|
|
const res: any = await queryBatchList({
|
pageNum: 1,
|
pageSize: 10,
|
params: {
|
startTime: formatDate(date) + ' 00:00:00',
|
endTime: formatDate(date) + ' 23:59:59'
|
}
|
});
|
if (!res || !res.rows) {
|
return;
|
}
|
batchList.value = res.rows;
|
|
checkNum.value = res.rows.reduce((sum, item) => sum + (item.num || 0), 0);
|
|
ngCountNum.value = res.rows.reduce((sum, item) => sum + (item.ngNum || 0), 0);
|
// 计算oknum总和
|
const totalOkNum = res.rows.reduce((sum, item) => sum + (item.okNum || 0), 0);
|
// 计算良品率 (避免除以0的情况)
|
const yieldRate = checkNum.value > 0 ? (totalOkNum / checkNum.value) * 100 : 0;
|
qualityRate.value = Number(yieldRate.toFixed(2));
|
batchScore.value = Number(((yieldRate/10 )-0.1).toFixed(1));
|
if(batchScore.value == -0.1){
|
batchScore.value = 0;
|
}
|
loading.value = false;
|
};
|
|
const getPwbatch = async () => {
|
const res: any = await queryPwbatchList();
|
if (res && res.yAxis) {
|
updatePwbatchData(res.xAxis, res.yAxis);
|
}
|
};
|
|
|
const getNgrank = async () => {
|
const res: any = await queryNgrank();
|
if (res && res.yAxis) {
|
updateNgrankData(res.xAxis, res.yAxis);
|
}
|
};
|
|
const formatDate = (date) => {
|
const year = date.getFullYear();
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0');
|
return `${year}-${month}-${day}`;
|
};
|
|
function updateHealthData(xAxis, yAxis) {
|
healthChart.value.setOption({
|
xAxis: {
|
type: 'category',
|
data: xAxis
|
},
|
series: [
|
{
|
data: yAxis
|
}
|
]
|
});
|
}
|
|
|
function updatePwbatchData(xAxis, yAxis) {
|
batchRateChart.value.setOption({
|
xAxis: {
|
type: 'category',
|
data: xAxis
|
},
|
series: [
|
{
|
data: yAxis
|
}
|
]
|
});
|
}
|
|
|
function updateNgrankData(xAxis, yAxis) {
|
ngChart.value.setOption({
|
yAxis: {
|
type: 'category',
|
data: xAxis,
|
inverse: true,
|
},
|
series: [
|
{
|
data: yAxis
|
}
|
]
|
});
|
}
|
|
onMounted(() => {
|
queryWarnBatchList();
|
initHealthChart();
|
initBatchRateChart();
|
initNgChart();
|
getHealth();
|
getBatch();
|
getPwbatch();
|
getNgrank();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.h-500 {
|
height: 500px;
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
font-weight: bold;
|
}
|
|
.grid {
|
display: grid;
|
}
|
|
.grid-cols-1 {
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
}
|
|
.grid-cols-2 {
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
}
|
|
.gap-4 {
|
gap: 1rem;
|
}
|
|
.mb-6 {
|
margin-bottom: 1.5rem;
|
}
|
|
.p-5 {
|
padding: 1.25rem;
|
}
|
|
.w-full {
|
width: 100%;
|
}
|
|
.h-400 {
|
height: 400px;
|
}
|
|
.h-420 {
|
height: 420px;
|
}
|
|
.bg-blue-50 {
|
background-color: #eff6ff;
|
}
|
|
.bg-yellow-50 {
|
background-color: #fffbeb;
|
}
|
|
.bg-red-50 {
|
background-color: #fef2f2;
|
}
|
|
.bg-green-50 {
|
background-color: #f0fdf4;
|
}
|
|
.rounded {
|
border-radius: 0.25rem;
|
}
|
|
.text-sm {
|
font-size:14px;
|
}
|
|
.text-xl {
|
font-size: 14px;
|
}
|
|
.font-bold {
|
font-weight: 700;
|
}
|
|
.text-gray-600 {
|
color: #4b5563;
|
}
|
|
@media (min-width: 768px) {
|
.md\:grid-cols-2 {
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
}
|
}
|
|
.statistic-footer {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
flex-wrap: wrap;
|
font-size: 12px;
|
color: var(--el-text-color-regular);
|
margin-top: 16px;
|
}
|
|
.statistic-footer .footer-item {
|
font-size: 14px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.statistic-footer .footer-item span:last-child {
|
display: inline-flex;
|
align-items: center;
|
margin-left: 4px;
|
}
|
|
.green {
|
color: var(--el-color-success);
|
}
|
|
.red {
|
color: var(--el-color-error);
|
}
|
</style>
|