feat(看板管理): 添加无缝滚动组件并优化看板样式

添加vue-seamless-scroll组件实现表格无缝滚动,优化看板布局和样式适配1920*1080分辨率
移除自定义滚动逻辑,增加时间范围选择功能,调整表格结构为固定表头+滚动内容
This commit is contained in:
赵正易 2025-11-01 20:04:58 +08:00
parent e01dd950b8
commit 7a3429dbe0
4 changed files with 477 additions and 327 deletions

View File

@ -1,6 +1,7 @@
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
import Cookies from 'js-cookie'
import vueSeamlessScroll from 'vue-seamless-scroll'
import Element from 'element-ui'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
@ -126,6 +127,7 @@ Vue.component('v-charts', ECharts)
Vue.use(VueCompositionAPI)
Vue.use(permission)
Vue.use(plugins)
Vue.use(vueSeamlessScroll)
Vue.use(Element, {
size: Cookies.get('size') || 'small', // set element-ui default size
})

View File

@ -36,17 +36,42 @@
<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">
<div
class="table-section"
style="flex: 3"
>
<div class="section-title">质量统计列表</div>
<div class="custom-table-wrapper">
<div class="custom-table-wrapper" ref="tableWrapper">
<!-- 固定表头 -->
<table
class="custom-table"
class="custom-table custom-table-header"
v-if="!loading"
>
<thead>
@ -66,28 +91,39 @@
<th width="108">结束时间</th>
</tr>
</thead>
<tbody ref="tableBody">
<tr
v-for="(row, index) in qualityStatisticsTable"
:key="index"
class="table-row"
>
<td>{{ row.workorderId || "-" }}</td>
<td>{{ row.finishedPartNumber || "-" }}</td>
<td>{{ row.color || "-" }}</td>
<td class="ellipsis">{{ row.productDescription || "-" }}</td>
<td>{{ row.team || "-" }}</td>
<td>{{ row.requireNumber || "-" }}</td>
<td>{{ row.qualifiedNumber || "-" }}</td>
<td>{{ offsetRate(row.qualifiedRate) }}%</td>
<td>{{ row.paoguangTotal || "-" }}</td>
<td>{{ row.damoTotal || "-" }}</td>
<td>{{ row.baofeiTotal || "-" }}</td>
<td>{{ row.startTime || "-" }}</td>
<td>{{ row.endTime || "-" }}</td>
</tr>
</tbody>
</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"
@ -102,7 +138,7 @@
<div class="charts-section">
<!-- 上半部分合格率分布饼图 -->
<div class="chart-container">
<div class="section-title">合格率分布</div>
<div class="section-title">合格率分布(86%/86%)</div>
<div
id="qualifiedRatePieChart"
style="width: 100%; height: 200px"
@ -126,11 +162,14 @@
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 {
@ -138,8 +177,8 @@ export default {
qualityStatisticsTable: [],
tableHeight: "500px",
search: {
starttime: null,
endtime: null,
startTime: null,
endTime: null,
team: null,
workorderid: null,
partnumber: null,
@ -157,31 +196,21 @@ export default {
totalGrind: 0,
totalScrap: 0,
},
//
timeRange: "daily", // 'daily''weekly'
allDataList: [],
//
qualifiedRatePieChart: null,
top5Chart: null,
tableScrollTimer: null,
refreshTimer: null,
scrollCheckTimer: null,
};
},
mounted() {
console.log("质量组件mounted生命周期调用");
this.init();
this.getQualityData();
//
window.addEventListener("resize", this.handleResize);
//
this.scrollCheckTimer = setInterval(() => {
console.log("质量组件定时检查滚动");
this.$nextTick(() => {
this.startTableAutoScroll();
});
}, 10000);
// 5
this.refreshTimer = setInterval(() => {
this.getQualityData();
@ -189,15 +218,12 @@ export default {
},
updated() {
//
//
this.$nextTick(() => {
console.log("质量组件updated生命周期调用");
if (this.qualityStatisticsTable.length > 0) {
this.initQualifiedRatePieChart();
this.initTop5Chart();
}
//
this.startTableAutoScroll();
});
},
@ -212,29 +238,37 @@ export default {
this.top5Chart.dispose();
this.top5Chart = null;
}
// setInterval
if (this.tableScrollTimer) {
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
//
if (this.scrollCheckTimer) {
clearInterval(this.scrollCheckTimer);
this.scrollCheckTimer = 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)direction01
singleWidth: 0, // (0)direction23
waitTime: 1000, // (1000ms)
switchOffset: 0, //
autoPlay: true, //
switchSingleStep: 150, //
switchDelay: 3000, //
ease: "easeInOutQuad", //
easing: "linear", //
};
},
},
methods: {
init() {
//
const today = new Date();
this.search.starttime = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0);
this.search.endtime = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
//
this.setTimeRange("daily");
},
//
@ -245,12 +279,38 @@ export default {
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,
};
@ -260,13 +320,12 @@ export default {
if (res.code == 200) {
this.qualityStatisticsTable = res.data || [];
this.allDataList = res.data || [];
console.log("质量数据加载完成,共", this.qualityStatisticsTable.length, "条记录");
//
this.calculateStatistics();
//
this.$nextTick(() => {
console.log("质量数据加载后启动滚动");
this.startTableAutoScroll();
});
}
@ -349,9 +408,9 @@ export default {
//
const validData = this.allDataList.filter((item) => item.qualifiedRate !== null && item.qualifiedRate !== "" && !isNaN(item.qualifiedRate));
// 80%80%
const above80 = validData.filter((item) => parseFloat(item.qualifiedRate) >= 80).length;
const below80 = validData.filter((item) => parseFloat(item.qualifiedRate) < 80).length;
// 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: {
@ -361,7 +420,7 @@ export default {
legend: {
orient: "horizontal",
bottom: 10,
data: ["合格率≥80%", "合格率<80%"],
data: ["合格率≥86%", "合格率<86%"],
textStyle: {
color: "#00ffff",
fontSize: 12,
@ -394,8 +453,8 @@ export default {
},
},
data: [
{ value: above80, name: "合格率≥80%", itemStyle: { color: "#67c23a" } },
{ value: below80, name: "合格率<80%", itemStyle: { color: "#f56c6c" } },
{ value: above86, name: "合格率≥86%", itemStyle: { color: "#67c23a" } },
{ value: below86, name: "合格率<86%", itemStyle: { color: "#f56c6c" } },
].filter((item) => item.value > 0), // 0
},
],
@ -507,77 +566,8 @@ export default {
this.top5Chart.setOption(option);
},
// -
startTableAutoScroll() {
//
if (this.tableScrollTimer) {
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
//
this.$nextTick(() => {
// DOM
const tableSection = this.$el.querySelector(".table-section");
const wrapper = tableSection ? tableSection.querySelector(".custom-table-wrapper") : null;
if (!wrapper) {
return;
}
//
const scrollableHeight = wrapper.scrollHeight - wrapper.clientHeight;
if (scrollableHeight <= 0) {
return; //
}
//
wrapper.style.overflow = "auto";
wrapper.style.overflowX = "auto"; // auto
wrapper.style.scrollBehavior = "smooth";
//
wrapper.scrollTop = 0;
//
const scrollSpeed = 2;
// 使setInterval
this.tableScrollTimer = setInterval(() => {
try {
if (wrapper && wrapper.isConnected) {
// DOM
//
if (wrapper.scrollTop + 10 >= wrapper.scrollHeight - 600) {
//
wrapper.scrollTop = 0;
} else {
//
wrapper.scrollTop += scrollSpeed;
}
} else {
// DOM
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
} catch (error) {
//
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
}, 50);
});
},
//
handleResize() {
//
const windowHeight = window.innerHeight;
// padding
const availableHeight = windowHeight - 60;
//
this.tableHeight = Math.min(Math.max(availableHeight - 140, 260), 600) + "px";
//
if (this.qualifiedRatePieChart) {
this.qualifiedRatePieChart.resize();
@ -592,40 +582,51 @@ export default {
<style scoped>
.quality-statistics-card {
padding: 10px;
padding: 15px;
background: transparent;
width: 100%;
height: 96vh; /* 确保至少填满整个视口高度 */
height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 10px;
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: 120px !important;
height: 200px !important;
margin-bottom: 10px; /* 减小底部间距 */
}
/* 第二行布局 */
/* 第二行布局 - 1920*1080优化 */
.second-row {
display: flex;
gap: 15px;
gap: 20px;
flex: 1;
min-height: 0;
overflow: hidden;
}
.table-section {
flex: 3; /* 保持与原grid-template-columns: 3fr 1fr类似的比例 */
flex: 2; /* 调整为2:1的比例 */
min-width: 0; /* 允许flex项收缩 */
display: flex;
flex-direction: column;
}
.charts-section {
flex: 1; /* 保持与原grid-template-columns: 3fr 1fr类似的比例 */
flex: 1; /* 调整为2:1的比例 */
min-width: 0; /* 允许flex项收缩 */
}
@ -635,6 +636,56 @@ export default {
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 {
@ -719,29 +770,24 @@ export default {
/* 自定义表格样式 */
.custom-table-wrapper {
/* 确保表格固定高度不依赖flex:1避免内容超出屏幕 */
overflow-y: auto;
overflow-x: auto; /* 允许水平滚动以适应小屏幕 */
border-radius: 6px;
flex: 1;
overflow: hidden;
background: rgba(0, 60, 120, 0.8);
/* 确保高度已通过JS设置这里不再设置 */
min-width: min-content; /* 确保内容不会被压缩得太小 */
border-radius: 6px;
position: relative;
display: flex;
flex-direction: column;
}
.custom-table {
height: 260px;
/* 固定表头样式 */
.custom-table-header {
width: 100%;
border-collapse: collapse;
color: #ffffff;
background: rgba(0, 60, 120, 1);
}
.custom-table thead {
position: sticky;
top: 0;
z-index: 10;
}
.custom-table th {
.custom-table-header th {
background: rgba(0, 60, 120, 1);
color: #00ffff;
font-weight: bold;
@ -749,13 +795,43 @@ export default {
padding: 12px 5px;
border-bottom: 1px solid rgba(0, 255, 255, 0.3);
font-size: 13px;
box-sizing: border-box;
}
.custom-table td {
/* 内容区域样式 */
.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 {
@ -820,6 +896,46 @@ export default {
}
/* 响应式调整 */
/* 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列布局 */

View File

@ -43,8 +43,9 @@
<div class="table-section">
<div class="section-title">工单列表</div>
<div class="custom-table-wrapper">
<!-- 固定表头 -->
<table
class="custom-table"
class="custom-table custom-table-header"
v-if="!loading"
>
<thead>
@ -61,32 +62,43 @@
<th width="100">状态</th>
</tr>
</thead>
<tbody ref="tableBody">
<tr
v-for="(row, index) in workorderOnlineTable"
:key="index"
class="table-row"
>
<td>{{ index + 1 }}</td>
<td>{{ row.clientWorkorder || "-" }}</td>
<td>{{ row.blankNumber || "-" }}</td>
<td>{{ row.finishedPartNumber || "-" }}</td>
<td class="ellipsis">{{ row.productDescription || "-" }}</td>
<td>{{ row.colour || "-" }}</td>
<td>{{ row.specifications || "-" }}</td>
<td>{{ row.vehicleNumber || "-" }}</td>
<td>{{ row.previousNumber || "-" }}</td>
<td>
<span
class="status-tag"
:class="getStatusClass(row.status)"
>
{{ getStatusText(row.status) }}
</span>
</td>
</tr>
</tbody>
</table>
<!-- 可滚动的表格内容 -->
<div class="table-content-wrapper">
<vue-seamless-scroll
:data="workorderOnlineTable"
:class-option="scrollOptions"
class="table-scroll"
>
<table class="custom-table custom-table-body">
<tbody>
<tr
v-for="(row, index) in workorderOnlineTable"
:key="index"
class="table-row"
>
<td width="60">{{ index + 1 }}</td>
<td width="130">{{ row.clientWorkorder || "-" }}</td>
<td width="120">{{ row.blankNumber || "-" }}</td>
<td width="150">{{ row.finishedPartNumber || "-" }}</td>
<td class="ellipsis">{{ row.productDescription || "-" }}</td>
<td width="100">{{ row.colour || "-" }}</td>
<td width="100">{{ row.specifications || "-" }}</td>
<td width="80">{{ row.vehicleNumber || "-" }}</td>
<td width="80">{{ row.previousNumber || "-" }}</td>
<td width="100">
<span
class="status-tag"
:class="getStatusClass(row.status)"
>
{{ getStatusText(row.status) }}
</span>
</td>
</tr>
</tbody>
</table>
</vue-seamless-scroll>
</div>
<div
class="loading-overlay"
v-if="loading"
@ -113,11 +125,14 @@
import { getWorkOrderCarouselBoardData } 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: "WorkorderOnlineCard",
components: {
kbHeader,
vueSeamlessScroll,
},
data() {
@ -126,9 +141,14 @@ export default {
workorderOnlineTable: [],
workorderChart: null,
productionChart: null,
tableScrollTimer: null,
refreshTimer: null,
scrollCheckTimer: null,
//
search: {
factoryCode: "",
lineCode: "",
startTime: "",
endTime: "",
},
//
todayStatistics: {
totalPlan: 0, //
@ -139,24 +159,9 @@ export default {
};
},
mounted() {
console.log("工单组件mounted生命周期调用");
this.init();
this.getWorkorderData();
window.addEventListener("resize", this.handleResize);
//
setTimeout(() => {
this.$nextTick(() => {
console.log("延迟启动滚动");
this.startTableAutoScroll();
});
}, 1000);
// 10
this.scrollCheckTimer = setInterval(() => {
console.log("工单组件定时检查滚动");
this.$nextTick(() => {
this.startTableAutoScroll();
});
}, 5000); // 5
// 5
this.refreshTimer = setInterval(() => {
@ -165,15 +170,12 @@ export default {
},
updated() {
//
//
this.$nextTick(() => {
console.log("工单组件updated生命周期调用");
if (this.workorderOnlineTable.length > 0) {
this.initWorkorderChart();
this.initProductionTypeChart();
}
//
this.startTableAutoScroll();
});
},
beforeDestroy() {
@ -187,27 +189,61 @@ export default {
this.productionChart.dispose();
this.productionChart = null;
}
// setInterval
if (this.tableScrollTimer) {
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
//
if (this.scrollCheckTimer) {
clearInterval(this.scrollCheckTimer);
this.scrollCheckTimer = 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)direction01
singleWidth: 0, // (0)direction23
waitTime: 1000, // (1000ms)
switchOffset: 0, //
autoPlay: true, //
switchSingleStep: 150, //
switchDelay: 3000, //
ease: "easeInOutQuad", //
easing: "linear", //
};
},
},
methods: {
init() {
//
this.setDefaultTimeRange();
},
//
setDefaultTimeRange() {
const today = dayjs();
const yesterday = today.subtract(1, "day");
this.search.startTime = yesterday.startOf("day").toDate();
this.search.endTime = today.endOf("day").toDate();
},
//
formatDateForBackend(date) {
return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
},
//
getWorkorderData() {
this.loading = true;
//
//
let query = {
...this.search,
startTime: this.formatDateForBackend(this.search.startTime),
endTime: this.formatDateForBackend(this.search.endTime),
pageNum: 1,
pageSize: 20,
};
@ -216,16 +252,11 @@ export default {
.then((res) => {
if (res.code == 200) {
this.workorderOnlineTable = res.data || [];
console.log("工单数据加载完成,共", this.workorderOnlineTable.length, "条记录");
//
this.calculateTodayStatistics();
//
this.$nextTick(() => {
console.log("数据加载后启动滚动");
this.startTableAutoScroll();
});
// vue-seamless-scroll
}
})
.finally(() => {
@ -448,82 +479,7 @@ export default {
this.productionChart.setOption(option);
},
// -
startTableAutoScroll() {
//
if (this.tableScrollTimer) {
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
//
this.$nextTick(() => {
// DOM
const tableSection = this.$el.querySelector(".table-section");
const wrapper = tableSection ? tableSection.querySelector(".custom-table-wrapper") : null;
if (!wrapper) {
return;
}
//
const scrollableHeight = wrapper.scrollHeight - wrapper.clientHeight;
if (scrollableHeight <= 0) {
return; //
}
//
wrapper.style.overflow = "auto";
wrapper.style.overflowX = "auto"; // auto
wrapper.style.scrollBehavior = "smooth";
//
wrapper.scrollTop = 0;
//
const scrollSpeed = 2;
// 使setInterval
this.tableScrollTimer = setInterval(() => {
try {
if (wrapper && wrapper.isConnected) {
// DOM
//
console.log(wrapper.scrollTop, wrapper.scrollHeight, wrapper.clientHeight);
if (wrapper.scrollTop + 10 >= wrapper.scrollHeight - 600) {
//
wrapper.scrollTop = 0;
} else {
//
wrapper.scrollTop += scrollSpeed;
}
} else {
// DOM
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
} catch (error) {
//
clearInterval(this.tableScrollTimer);
this.tableScrollTimer = null;
}
}, 50);
});
},
handleResize() {
//
const windowHeight = window.innerHeight;
// padding
const availableHeight = windowHeight - 60;
//
const firstRow = document.querySelector(".first-row");
if (firstRow) {
firstRow.style.height = "250px";
}
//
this.tableHeight = Math.min(Math.max(availableHeight - 200, 260), 600) + "px";
//
if (this.workorderChart) {
this.workorderChart.resize();
@ -538,24 +494,25 @@ export default {
<style scoped>
.workorder-online-card {
padding: 10px;
padding: 15px;
background: transparent;
width: 100%;
height: 96vh; /* 确保至少填满整个视口高度 */
height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 15px;
overflow-x: auto; /* 允许容器水平滚动,确保内容不被截断 */
gap: 20px;
overflow: hidden;
}
/* kbHeader组件已包含标题样式 */
/* 第一行布局 */
/* 第一行布局 - 1920*1080优化 */
.first-row {
display: flex;
gap: 15px;
height: 200px;
gap: 20px;
height: 250px;
flex-shrink: 0;
}
.chart-section {
@ -568,17 +525,20 @@ export default {
min-width: 0;
}
/* 第二行布局 */
/* 第二行布局 - 1920*1080优化 */
.second-row {
display: flex;
gap: 15px;
gap: 20px;
flex: 1;
min-height: 0;
overflow: hidden;
}
.table-section {
flex: 2;
min-width: 0;
display: flex;
flex-direction: column;
}
.bar-chart-section {
@ -662,29 +622,24 @@ export default {
/* 自定义表格样式 */
.custom-table-wrapper {
/* 确保表格固定高度不依赖flex:1避免内容超出屏幕 */
overflow-y: auto;
overflow-x: auto; /* 允许水平滚动以适应小屏幕 */
border-radius: 6px;
background: rgba(0, 60, 120, 0.8);
/* 确保高度已通过JS设置这里不再设置 */
min-width: min-content; /* 确保内容不会被压缩得太小 */
min-width: min-content;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.custom-table {
height: 260px;
/* 固定表头样式 */
.custom-table-header {
width: 100%;
border-collapse: collapse;
color: #ffffff;
background: rgba(0, 60, 120, 1);
}
.custom-table thead {
position: sticky;
top: 0;
z-index: 10;
}
.custom-table th {
.custom-table-header th {
background: rgba(0, 60, 120, 1);
color: #00ffff;
font-weight: bold;
@ -692,6 +647,43 @@ export default {
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% - 44px); /* 减去表头高度 */
}
/* 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;
}
.custom-table td {
@ -790,6 +782,42 @@ export default {
}
/* 响应式调整 */
/* 1920*1080分辨率专属样式 */
@media (min-width: 1920px) and (min-height: 1080px) {
.workorder-online-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;
}
}
/* 大屏响应式调整 */
@media (min-width: 1600px) and (max-width: 1919px) {
.first-row {
height: 220px;
}
.flip-card-value {
font-size: 24px;
}
}
/* 中小屏响应式保持不变 */
@media (max-width: 1400px) {
.first-row {
flex-direction: row;

View File

@ -61,8 +61,8 @@ export default {
padding: 0;
margin: 0;
background: linear-gradient(135deg, #001529 0%, #002140 100%);
overflow: auto;
box-sizing: border-box;
overflow: hidden;
}
.dashboard-border {
@ -75,11 +75,15 @@ export default {
overflow: hidden;
}
/* 优化轮播项样式 */
/* 优化轮播项样式 - 1920*1080适配 */
.el-carousel__item {
padding: 0;
box-sizing: border-box;
min-height: 100vh;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
/* 确保指示器清晰可见 */