添加vue-seamless-scroll组件实现表格无缝滚动,优化看板布局和样式适配1920*1080分辨率 移除自定义滚动逻辑,增加时间范围选择功能,调整表格结构为固定表头+滚动内容
999 lines
31 KiB
Vue
999 lines
31 KiB
Vue
<template>
|
||
<div class="quality-statistics-card">
|
||
<!-- 标题部分:使用kbHeader组件统一风格 -->
|
||
<kb-header>质量统计报表</kb-header>
|
||
|
||
<!-- 第一行:统计数据翻牌器区域 -->
|
||
<div class="first-row">
|
||
<div class="statistics-section">
|
||
<div class="flip-card-container">
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">报表数量</div>
|
||
<div class="flip-card-value">{{ statistics.totalRecords }}</div>
|
||
</div>
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">投入总数</div>
|
||
<div class="flip-card-value">{{ statistics.totalInput }}</div>
|
||
</div>
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">合格总数</div>
|
||
<div class="flip-card-value completed">{{ statistics.totalQualified }}</div>
|
||
</div>
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">合格率</div>
|
||
<div class="flip-card-value">{{ statistics.passRate }}</div>
|
||
</div>
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">抛光总数</div>
|
||
<div class="flip-card-value">{{ statistics.totalPolish }}</div>
|
||
</div>
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">打磨总数</div>
|
||
<div class="flip-card-value">{{ statistics.totalGrind }}</div>
|
||
</div>
|
||
<div class="flip-card">
|
||
<div class="flip-card-label">报废总数</div>
|
||
<div class="flip-card-value scrap">{{ statistics.totalScrap }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 时间范围切换 -->
|
||
<div class="time-range-switch">
|
||
<div class="switch-label">数据范围:</div>
|
||
<div class="switch-options">
|
||
<div
|
||
class="switch-option"
|
||
:class="{ active: timeRange === 'daily' }"
|
||
@click="setTimeRange('daily')"
|
||
>
|
||
每日
|
||
</div>
|
||
<div
|
||
class="switch-option"
|
||
:class="{ active: timeRange === 'weekly' }"
|
||
@click="setTimeRange('weekly')"
|
||
>
|
||
每周
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第二行:表格和图表区域 -->
|
||
<div class="second-row">
|
||
<!-- 左侧:质量统计表格 -->
|
||
<div
|
||
class="table-section"
|
||
style="flex: 3"
|
||
>
|
||
<div class="section-title">质量统计列表</div>
|
||
<div class="custom-table-wrapper" ref="tableWrapper">
|
||
<!-- 固定表头 -->
|
||
<table
|
||
class="custom-table custom-table-header"
|
||
v-if="!loading"
|
||
>
|
||
<thead>
|
||
<tr>
|
||
<th width="140">工单号</th>
|
||
<th width="120">零件号</th>
|
||
<th width="100">颜色</th>
|
||
<th width="150">描述</th>
|
||
<th width="80">班次</th>
|
||
<th width="120">生产投入数</th>
|
||
<th width="80">合格数</th>
|
||
<th width="80">合格率</th>
|
||
<th width="80">抛光总数</th>
|
||
<th width="80">打磨总数</th>
|
||
<th width="80">报废总数</th>
|
||
<th width="108">开始时间</th>
|
||
<th width="108">结束时间</th>
|
||
</tr>
|
||
</thead>
|
||
</table>
|
||
<!-- 可滚动的表格内容 -->
|
||
<div class="table-content-wrapper">
|
||
<vue-seamless-scroll
|
||
:data="qualityStatisticsTable"
|
||
:class-option="scrollOptions"
|
||
class="table-scroll"
|
||
>
|
||
<table class="custom-table custom-table-body">
|
||
<tbody>
|
||
<tr
|
||
v-for="(row, index) in qualityStatisticsTable"
|
||
:key="index"
|
||
class="table-row"
|
||
>
|
||
<td width="140">{{ row.workorderId || "-" }}</td>
|
||
<td width="120">{{ row.finishedPartNumber || "-" }}</td>
|
||
<td width="100">{{ row.color || "-" }}</td>
|
||
<td width="150" class="ellipsis">{{ row.productDescription || "-" }}</td>
|
||
<td width="80">{{ row.team || "-" }}</td>
|
||
<td width="120">{{ row.requireNumber || "-" }}</td>
|
||
<td width="80">{{ row.qualifiedNumber || "-" }}</td>
|
||
<td width="80">{{ offsetRate(row.qualifiedRate) }}%</td>
|
||
<td width="80">{{ row.paoguangTotal || "-" }}</td>
|
||
<td width="80">{{ row.damoTotal || "-" }}</td>
|
||
<td width="80">{{ row.baofeiTotal || "-" }}</td>
|
||
<td width="108">{{ row.startTime || "-" }}</td>
|
||
<td width="108">{{ row.endTime || "-" }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</vue-seamless-scroll>
|
||
</div>
|
||
<div
|
||
class="loading-overlay"
|
||
v-if="loading"
|
||
>
|
||
<div class="loading-spinner"></div>
|
||
<div class="loading-text">加载中...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧:图表区域 -->
|
||
<div class="charts-section">
|
||
<!-- 上半部分:合格率分布饼图 -->
|
||
<div class="chart-container">
|
||
<div class="section-title">合格率分布(≥86%/<86%)</div>
|
||
<div
|
||
id="qualifiedRatePieChart"
|
||
style="width: 100%; height: 200px"
|
||
></div>
|
||
</div>
|
||
|
||
<!-- 下半部分:平均合格率TOP5柱状图 -->
|
||
<div class="chart-container">
|
||
<div class="section-title">平均合格率TOP5</div>
|
||
<div
|
||
id="top5Chart"
|
||
style="width: 100%; height: 280px"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { getQualityStatisticsCarouselBoardData } from "@/api/kanbanManagement/carouselBoard.js";
|
||
import * as echarts from "echarts";
|
||
import kbHeader from "./kbHeader.vue";
|
||
import dayjs from "dayjs";
|
||
import vueSeamlessScroll from "vue-seamless-scroll";
|
||
|
||
export default {
|
||
name: "QualityStatisticsCard",
|
||
components: {
|
||
kbHeader,
|
||
vueSeamlessScroll,
|
||
},
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
qualityStatisticsTable: [],
|
||
tableHeight: "500px",
|
||
search: {
|
||
startTime: null,
|
||
endTime: null,
|
||
team: null,
|
||
workorderid: null,
|
||
partnumber: null,
|
||
sortType: 0,
|
||
reportType: 0,
|
||
product_description: "",
|
||
},
|
||
// 统计数据对象
|
||
statistics: {
|
||
totalRecords: 0,
|
||
totalInput: 0,
|
||
totalQualified: 0,
|
||
passRate: "0%",
|
||
totalPolish: 0,
|
||
totalGrind: 0,
|
||
totalScrap: 0,
|
||
},
|
||
// 时间范围选择
|
||
timeRange: "daily", // 'daily'或'weekly'
|
||
allDataList: [],
|
||
// 图表实例
|
||
qualifiedRatePieChart: null,
|
||
top5Chart: null,
|
||
refreshTimer: null,
|
||
};
|
||
},
|
||
|
||
mounted() {
|
||
this.init();
|
||
this.getQualityData();
|
||
window.addEventListener("resize", this.handleResize);
|
||
|
||
// 设置5分钟定时刷新
|
||
this.refreshTimer = setInterval(() => {
|
||
this.getQualityData();
|
||
}, 5 * 60 * 1000); // 5分钟 = 5 * 60 * 1000毫秒
|
||
},
|
||
|
||
updated() {
|
||
// 数据更新后初始化图表
|
||
this.$nextTick(() => {
|
||
if (this.qualityStatisticsTable.length > 0) {
|
||
this.initQualifiedRatePieChart();
|
||
this.initTop5Chart();
|
||
}
|
||
});
|
||
},
|
||
|
||
beforeDestroy() {
|
||
window.removeEventListener("resize", this.handleResize);
|
||
// 清理图表实例
|
||
if (this.qualifiedRatePieChart) {
|
||
this.qualifiedRatePieChart.dispose();
|
||
this.qualifiedRatePieChart = null;
|
||
}
|
||
if (this.top5Chart) {
|
||
this.top5Chart.dispose();
|
||
this.top5Chart = null;
|
||
}
|
||
// 清理数据刷新定时器
|
||
if (this.refreshTimer) {
|
||
clearInterval(this.refreshTimer);
|
||
this.refreshTimer = null;
|
||
}
|
||
},
|
||
computed: {
|
||
// vue-seamless-scroll配置
|
||
scrollOptions() {
|
||
return {
|
||
step: 0.5, // 滚动速度,值越大速度越快
|
||
limitMoveNum: 1, // 开始无缝滚动的数据量,滚动的数据列表要大于等于这个值
|
||
hoverStop: false, // 是否开启鼠标悬停停止
|
||
direction: 1, // 0 向下 1 向上 2 向左 3 向右
|
||
openWatch: true, // 开启数据监听,数据变化时自动滚动
|
||
singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动),只有direction为0或1时生效
|
||
singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动),只有direction为2或3时生效
|
||
waitTime: 1000, // 单步运动停止的时间(默认值1000ms)
|
||
switchOffset: 0, // 左右切换的偏移量
|
||
autoPlay: true, // 是否自动播放
|
||
switchSingleStep: 150, // 切换步数
|
||
switchDelay: 3000, // 切换延迟
|
||
ease: "easeInOutQuad", // 缓动函数
|
||
easing: "linear", // 动画过渡效果
|
||
};
|
||
},
|
||
},
|
||
methods: {
|
||
init() {
|
||
// 根据默认时间范围设置时间
|
||
this.setTimeRange("daily");
|
||
},
|
||
|
||
// 合格率去除小数
|
||
offsetRate(num) {
|
||
if (num === null || num === "" || isNaN(num)) {
|
||
return "0";
|
||
}
|
||
return parseInt(num);
|
||
},
|
||
|
||
// 设置时间范围
|
||
setTimeRange(type) {
|
||
this.timeRange = type;
|
||
const today = dayjs();
|
||
|
||
if (type === "daily") {
|
||
// 今天
|
||
this.search.startTime = today.startOf("day").toDate();
|
||
this.search.endTime = today.endOf("day").toDate();
|
||
} else if (type === "weekly") {
|
||
// 本周(周一到周日)
|
||
this.search.startTime = today.startOf("week").toDate();
|
||
this.search.endTime = today.endOf("week").toDate();
|
||
}
|
||
|
||
// 重新查询数据
|
||
this.getQualityData();
|
||
},
|
||
|
||
// 格式化日期为字符串(传给后台)
|
||
formatDateForBackend(date) {
|
||
return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
|
||
},
|
||
|
||
// 获取质量数据
|
||
getQualityData() {
|
||
this.loading = true;
|
||
// 设置默认查询参数,将日期转换为字符串格式
|
||
let query = {
|
||
...this.search,
|
||
startTime: this.formatDateForBackend(this.search.startTime),
|
||
endTime: this.formatDateForBackend(this.search.endTime),
|
||
pageNum: 1,
|
||
pageSize: 20,
|
||
};
|
||
|
||
getQualityStatisticsCarouselBoardData(query)
|
||
.then((res) => {
|
||
if (res.code == 200) {
|
||
this.qualityStatisticsTable = res.data || [];
|
||
this.allDataList = res.data || [];
|
||
|
||
// 计算统计数据
|
||
this.calculateStatistics();
|
||
|
||
// 数据加载完成后立即启动滚动
|
||
this.$nextTick(() => {
|
||
this.startTableAutoScroll();
|
||
});
|
||
}
|
||
})
|
||
.finally(() => {
|
||
this.loading = false;
|
||
});
|
||
},
|
||
|
||
// 计算统计数据
|
||
calculateStatistics() {
|
||
// 过滤掉倒车雷达数据
|
||
const filteredData = this.allDataList.filter((data) => !data.productDescription?.includes("倒车雷达"));
|
||
|
||
// 统计报表数量
|
||
this.statistics.totalRecords = filteredData.length;
|
||
|
||
// 确保准确计算数字翻牌器数据
|
||
// 统计投入数
|
||
this.statistics.totalInput = Math.trunc(
|
||
filteredData.reduce((acc, data) => {
|
||
// 确保数据准确性,只加有效数字
|
||
const value = Number(data.requireNumber);
|
||
return acc + (isNaN(value) ? 0 : value);
|
||
}, 0) / 3
|
||
);
|
||
|
||
// 统计合格数
|
||
this.statistics.totalQualified = Math.trunc(
|
||
filteredData.reduce((acc, data) => {
|
||
const value = Number(data.qualifiedNumber);
|
||
return acc + (isNaN(value) ? 0 : value);
|
||
}, 0) / 3
|
||
);
|
||
|
||
// 计算合格率
|
||
if (this.statistics.totalInput > 0) {
|
||
const rate = (this.statistics.totalQualified / this.statistics.totalInput) * 100;
|
||
this.statistics.passRate = rate.toFixed(1) + "%";
|
||
} else {
|
||
this.statistics.passRate = "0%";
|
||
}
|
||
|
||
// 统计抛光数
|
||
this.statistics.totalPolish = Math.trunc(
|
||
filteredData.reduce((acc, data) => {
|
||
const value = Number(data.paoguangTotal);
|
||
return acc + (isNaN(value) ? 0 : value);
|
||
}, 0) / 3
|
||
);
|
||
|
||
// 统计打磨数
|
||
this.statistics.totalGrind = Math.trunc(
|
||
filteredData.reduce((acc, data) => {
|
||
const value = Number(data.damoTotal);
|
||
return acc + (isNaN(value) ? 0 : value);
|
||
}, 0) / 3
|
||
);
|
||
|
||
// 统计报废数
|
||
this.statistics.totalScrap = Math.trunc(
|
||
filteredData.reduce((acc, data) => {
|
||
const value = Number(data.baofeiTotal);
|
||
return acc + (isNaN(value) ? 0 : value);
|
||
}, 0) / 3
|
||
);
|
||
},
|
||
|
||
// 初始化合格率分布饼图(80%以上和以下的占比)
|
||
initQualifiedRatePieChart() {
|
||
const chartDom = document.getElementById("qualifiedRatePieChart");
|
||
if (!chartDom) return;
|
||
|
||
if (this.qualifiedRatePieChart) {
|
||
this.qualifiedRatePieChart.dispose();
|
||
}
|
||
|
||
this.qualifiedRatePieChart = echarts.init(chartDom);
|
||
|
||
// 过滤有合格数据的记录
|
||
const validData = this.allDataList.filter((item) => item.qualifiedRate !== null && item.qualifiedRate !== "" && !isNaN(item.qualifiedRate));
|
||
|
||
// 统计合格率86%以上和86%以下的数量
|
||
const above86 = validData.filter((item) => parseFloat(item.qualifiedRate) >= 86).length;
|
||
const below86 = validData.filter((item) => parseFloat(item.qualifiedRate) < 86).length;
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: "item",
|
||
formatter: "{b}: {c} ({d}%)",
|
||
},
|
||
legend: {
|
||
orient: "horizontal",
|
||
bottom: 10,
|
||
data: ["合格率≥86%", "合格率<86%"],
|
||
textStyle: {
|
||
color: "#00ffff",
|
||
fontSize: 12,
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
name: "合格率分布",
|
||
type: "pie",
|
||
radius: ["40%", "70%"],
|
||
center: ["50%", "40%"],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 6,
|
||
borderColor: "rgba(0, 20, 40, 0.6)",
|
||
borderWidth: 2,
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: "{b}\n{d}%",
|
||
color: "#ffffff",
|
||
fontSize: 12,
|
||
},
|
||
emphasis: {
|
||
label: {
|
||
show: true,
|
||
fontSize: "14",
|
||
fontWeight: "bold",
|
||
color: "#00ffff",
|
||
},
|
||
},
|
||
data: [
|
||
{ value: above86, name: "合格率≥86%", itemStyle: { color: "#67c23a" } },
|
||
{ value: below86, name: "合格率<86%", itemStyle: { color: "#f56c6c" } },
|
||
].filter((item) => item.value > 0), // 过滤掉数量为0的类别
|
||
},
|
||
],
|
||
};
|
||
|
||
this.qualifiedRatePieChart.setOption(option);
|
||
},
|
||
|
||
// 初始化平均合格率TOP5横向柱状图
|
||
initTop5Chart() {
|
||
const chartDom = document.getElementById("top5Chart");
|
||
if (!chartDom) return;
|
||
|
||
if (this.top5Chart) {
|
||
this.top5Chart.dispose();
|
||
}
|
||
|
||
this.top5Chart = echarts.init(chartDom);
|
||
|
||
// 过滤有合格数据的记录并按合格率降序排序,确保合格率高的显示在顶部
|
||
const validData = this.allDataList
|
||
.filter((item) => item.qualifiedRate !== null && item.qualifiedRate !== "" && !isNaN(item.qualifiedRate))
|
||
.sort((a, b) => parseFloat(b.qualifiedRate) - parseFloat(a.qualifiedRate));
|
||
|
||
// 获取合格率最高的前5个
|
||
const top5 = validData.slice(0, 5);
|
||
|
||
// 准备横向柱状图数据,y轴显示产品描述
|
||
const labels = top5.map((item) => {
|
||
const desc = item.productDescription || item.finishedPartNumber || "未知";
|
||
// 截取前20个字符,优先显示描述
|
||
const shortDesc = desc.length > 20 ? desc.substring(0, 20) + "..." : desc;
|
||
return shortDesc;
|
||
});
|
||
const rates = top5.map((item) => parseFloat(item.qualifiedRate) || 0);
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: "axis",
|
||
axisPointer: {
|
||
type: "shadow",
|
||
},
|
||
formatter: function (params) {
|
||
const data = params[0];
|
||
return data.name + "<br/>合格率: " + data.value + "%";
|
||
},
|
||
},
|
||
grid: {
|
||
left: "5%", // 减小左侧边距,让图表整体向左移动
|
||
right: "10%",
|
||
top: "10%",
|
||
bottom: "10%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
type: "value",
|
||
min: 0,
|
||
max: 100,
|
||
axisLabel: {
|
||
formatter: "{value}%",
|
||
color: "#ffffff",
|
||
fontSize: 12,
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
color: "rgba(255, 255, 255, 0.1)",
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "category",
|
||
data: labels,
|
||
axisLabel: {
|
||
color: "#00ffff",
|
||
fontSize: 12,
|
||
interval: 0,
|
||
formatter: function (value) {
|
||
// 自动换行,每行最多显示8个字符
|
||
const maxLength = 8;
|
||
let result = "";
|
||
for (let i = 0; i < value.length; i += maxLength) {
|
||
result += value.substring(i, i + maxLength) + "\n";
|
||
}
|
||
return result.trim();
|
||
},
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
name: "合格率",
|
||
type: "bar",
|
||
data: rates,
|
||
itemStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||
{ offset: 0, color: "#0066ff" },
|
||
{ offset: 1, color: "#00ffff" },
|
||
]),
|
||
},
|
||
barWidth: "50%",
|
||
label: {
|
||
show: true,
|
||
position: "right",
|
||
color: "#ffffff",
|
||
formatter: "{c}%",
|
||
},
|
||
},
|
||
],
|
||
};
|
||
|
||
this.top5Chart.setOption(option);
|
||
},
|
||
// 处理窗口大小变化
|
||
handleResize() {
|
||
// 调整图表大小
|
||
if (this.qualifiedRatePieChart) {
|
||
this.qualifiedRatePieChart.resize();
|
||
}
|
||
if (this.top5Chart) {
|
||
this.top5Chart.resize();
|
||
}
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.quality-statistics-card {
|
||
padding: 15px;
|
||
background: transparent;
|
||
width: 100%;
|
||
height: 100vh;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
overflow-x: auto; /* 允许容器水平滚动,确保内容不被截断 */
|
||
}
|
||
|
||
/* 时间范围切换样式 */
|
||
.time-range-switch {
|
||
padding: 8px 0;
|
||
text-align: center;
|
||
background: #f5f7fa;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
/* 第一行布局 - 1920*1080优化 */
|
||
.first-row {
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
height: 200px !important;
|
||
margin-bottom: 10px; /* 减小底部间距 */
|
||
}
|
||
|
||
/* 第二行布局 - 1920*1080优化 */
|
||
.second-row {
|
||
display: flex;
|
||
gap: 20px;
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.table-section {
|
||
flex: 2; /* 调整为2:1的比例 */
|
||
min-width: 0; /* 允许flex项收缩 */
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.charts-section {
|
||
flex: 1; /* 调整为2:1的比例 */
|
||
min-width: 0; /* 允许flex项收缩 */
|
||
}
|
||
|
||
/* 统计翻牌器样式 */
|
||
.statistics-section {
|
||
background: rgba(0, 20, 40, 0.6);
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
padding: 15px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 时间范围切换样式 */
|
||
.time-range-switch {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 20px;
|
||
padding: 10px;
|
||
background: rgba(0, 60, 120, 0.5);
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||
}
|
||
|
||
.switch-label {
|
||
color: #00ffff;
|
||
font-size: 14px;
|
||
margin-right: 15px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.switch-options {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.switch-option {
|
||
padding: 6px 20px;
|
||
background: rgba(0, 40, 80, 0.8);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 4px;
|
||
color: #ffffff;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.switch-option:hover {
|
||
background: rgba(0, 255, 255, 0.2);
|
||
border-color: #00ffff;
|
||
color: #00ffff;
|
||
}
|
||
|
||
.switch-option.active {
|
||
background: rgba(0, 255, 255, 0.3);
|
||
border-color: #00ffff;
|
||
color: #00ffff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.flip-card-container {
|
||
display: flex;
|
||
gap: 15px;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.flip-card {
|
||
flex: 1;
|
||
min-width: calc(100% / 7 - 15px * 6 / 7); /* 大致保持7列布局 */
|
||
min-height: 0; /* 允许flex项收缩 */
|
||
}
|
||
|
||
.flip-card {
|
||
background: rgba(0, 60, 120, 0.8);
|
||
border-radius: 6px;
|
||
padding: 15px 10px;
|
||
text-align: center;
|
||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.flip-card:hover {
|
||
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.flip-card-label {
|
||
color: #ffffff;
|
||
font-size: 12px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.flip-card-value {
|
||
color: #00ffff;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
font-family: "Courier New", monospace;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
.flip-card-value.completed {
|
||
color: #67c23a;
|
||
}
|
||
|
||
.flip-card-value.scrap {
|
||
color: #f56c6c;
|
||
}
|
||
|
||
/* 通用区域样式 */
|
||
.table-section,
|
||
.charts-section,
|
||
.chart-container {
|
||
background: rgba(0, 20, 40, 0.6);
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
padding: 15px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.charts-section {
|
||
gap: 15px;
|
||
}
|
||
|
||
.chart-container {
|
||
flex: 1;
|
||
min-height: 0;
|
||
}
|
||
|
||
.section-title {
|
||
color: #00ffff;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
margin-bottom: 15px;
|
||
text-align: center;
|
||
padding-bottom: 8px;
|
||
border-bottom: 1px solid rgba(0, 255, 255, 0.3);
|
||
}
|
||
|
||
/* 自定义表格样式 */
|
||
.custom-table-wrapper {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
background: rgba(0, 60, 120, 0.8);
|
||
border-radius: 6px;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 固定表头样式 */
|
||
.custom-table-header {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
color: #ffffff;
|
||
background: rgba(0, 60, 120, 1);
|
||
}
|
||
|
||
.custom-table-header th {
|
||
background: rgba(0, 60, 120, 1);
|
||
color: #00ffff;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
padding: 12px 5px;
|
||
border-bottom: 1px solid rgba(0, 255, 255, 0.3);
|
||
font-size: 13px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 内容区域样式 */
|
||
.table-content-wrapper {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
max-height: calc(100% - 46px); /* 减去表头高度 */
|
||
}
|
||
|
||
/* vue-seamless-scroll样式 */
|
||
.table-scroll {
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
|
||
/* 隐藏滚动条 */
|
||
.table-scroll::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
.table-scroll {
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.custom-table-body {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.custom-table-body td {
|
||
text-align: center;
|
||
padding: 10px 5px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||
font-size: 13px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.table-row:hover td {
|
||
background-color: rgba(0, 255, 255, 0.1);
|
||
}
|
||
|
||
/* 加载样式 */
|
||
.loading-overlay {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
color: #00ffff;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 3px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 50%;
|
||
border-top-color: #00ffff;
|
||
animation: spin 1s ease-in-out infinite;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 超出省略 */
|
||
.ellipsis {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-width: 150px;
|
||
}
|
||
|
||
/* 滚动条样式 */
|
||
.custom-table-wrapper::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.custom-table-wrapper::-webkit-scrollbar-track {
|
||
background: rgba(0, 60, 120, 0.5);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.custom-table-wrapper::-webkit-scrollbar-thumb {
|
||
background: rgba(0, 255, 255, 0.5);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.custom-table-wrapper::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(0, 255, 255, 0.8);
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
/* 1920*1080分辨率专属样式 */
|
||
@media (min-width: 1920px) and (min-height: 1080px) {
|
||
.quality-statistics-card {
|
||
padding: 20px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.flip-card-label {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.flip-card-value {
|
||
font-size: 28px;
|
||
}
|
||
|
||
.custom-table th,
|
||
.custom-table td {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.flip-card {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
|
||
/* 大屏响应式调整 */
|
||
@media (min-width: 1600px) and (max-width: 1919px) {
|
||
.first-row {
|
||
height: 200px !important;
|
||
}
|
||
|
||
.flip-card-value {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
|
||
/* 中小屏响应式调整 */
|
||
@media (max-width: 1400px) {
|
||
.flip-card {
|
||
min-width: calc(100% / 5 - 15px * 4 / 5); /* 调整为5列布局 */
|
||
}
|
||
}
|
||
|
||
@media (max-width: 1200px) {
|
||
.flip-card {
|
||
min-width: calc(100% / 4 - 15px * 3 / 4); /* 调整为4列布局 */
|
||
}
|
||
|
||
.second-row {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.table-section,
|
||
.charts-section {
|
||
flex: 1;
|
||
width: 100%;
|
||
}
|
||
|
||
.charts-section {
|
||
gap: 10px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 1024px) {
|
||
.flip-card {
|
||
min-width: calc(100% / 3 - 15px * 2 / 3); /* 调整为3列布局 */
|
||
}
|
||
|
||
.second-row {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.charts-section {
|
||
flex-direction: row;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.flip-card {
|
||
min-width: calc(100% / 2 - 15px * 1 / 2); /* 调整为2列布局 */
|
||
}
|
||
|
||
.charts-section {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.custom-table {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.custom-table th,
|
||
.custom-table td {
|
||
padding: 8px 2px;
|
||
font-size: 11px;
|
||
}
|
||
}
|
||
</style>
|