feat(轮播看板): 重构轮播看板组件并添加统一标题样式
重构轮播看板管理页面布局,添加kbHeader组件统一标题样式 新增header.png和header.svg作为看板标题背景 优化WorkorderOnlineCard和QualityStatisticsCard组件布局和样式 实现表格自动滚动功能,添加统计图表展示数据 调整轮播容器高度和间距,提升视觉效果
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,108 +1,167 @@
|
||||
<template>
|
||||
<div class="workorder-online-card">
|
||||
<dv-title text="生产计划工单" :color="['#00ffff', '#0066ff']" style="margin-bottom: 15px;" />
|
||||
<kb-header>生产计划工单</kb-header>
|
||||
|
||||
<!-- 今日统计数据区域 -->
|
||||
<div class="statistics-container">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">今日总计划数:</span>
|
||||
<span class="stat-value">{{ todayStatistics.totalPlan }}</span>
|
||||
<!-- 第一行布局 -->
|
||||
<div class="first-row">
|
||||
<!-- 第一行第一个区域:今日生产未完成数及已完成数占比饼图 -->
|
||||
<div class="chart-section">
|
||||
<div class="section-title">今日生产状态</div>
|
||||
<div id="workorderStatusChart" style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">已完成计划数:</span>
|
||||
<span class="stat-value completed">{{ todayStatistics.completedPlan }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">未完成计划数:</span>
|
||||
<span class="stat-value uncompleted">{{ todayStatistics.uncompletedPlan }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">今日总投入数:</span>
|
||||
<span class="stat-value">{{ todayStatistics.totalInput }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">今日总合格数:</span>
|
||||
<span class="stat-value qualified">{{ todayStatistics.totalQualified }}</span>
|
||||
|
||||
<!-- 第一行第二、三区域合并:今日统计数据翻牌器 -->
|
||||
<div class="statistics-section">
|
||||
<div class="section-title">今日统计</div>
|
||||
<div class="flip-card-container">
|
||||
<div class="flip-card">
|
||||
<div class="flip-card-label">今日总计划数</div>
|
||||
<div class="flip-card-value">{{ todayStatistics.totalPlan }}</div>
|
||||
</div>
|
||||
<div class="flip-card">
|
||||
<div class="flip-card-label">已完成计划数</div>
|
||||
<div class="flip-card-value completed">{{ todayStatistics.completedPlan }}</div>
|
||||
</div>
|
||||
<div class="flip-card">
|
||||
<div class="flip-card-label">未完成计划数</div>
|
||||
<div class="flip-card-value uncompleted">{{ todayStatistics.uncompletedPlan }}</div>
|
||||
</div>
|
||||
<div class="flip-card">
|
||||
<div class="flip-card-label">今日总投入数</div>
|
||||
<div class="flip-card-value">{{ todayStatistics.totalInput }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工单完成状态分布环形进度图 -->
|
||||
<div class="chart-container">
|
||||
<div id="workorderStatusChart" style="width: 100%; height: 200px;"></div>
|
||||
<!-- 第二行布局 -->
|
||||
<div class="second-row">
|
||||
<!-- 第一、二区域:工单表格 -->
|
||||
<div class="table-section">
|
||||
<div class="section-title">工单列表</div>
|
||||
<div class="custom-table-wrapper" :style="{ height: tableHeight }">
|
||||
<table class="custom-table" v-if="!loading">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="60">序号</th>
|
||||
<th width="130">工单号</th>
|
||||
<th width="120">毛坯号</th>
|
||||
<th width="150">成品零件号</th>
|
||||
<th>产品描述</th>
|
||||
<th width="100">颜色</th>
|
||||
<th width="100">规格</th>
|
||||
<th width="80">车数</th>
|
||||
<th width="80">上件数</th>
|
||||
<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="loading-overlay" v-if="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三区域:生产统计横向柱状图 -->
|
||||
<div class="bar-chart-section">
|
||||
<div class="section-title">生产零件Top5统计</div>
|
||||
<div id="productionTypeChart" style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="workorderOnlineTable"
|
||||
v-loading="loading"
|
||||
border
|
||||
style="width: 100%"
|
||||
:height="tableHeight"
|
||||
ref="workorderTable"
|
||||
stripe
|
||||
highlight-current-row
|
||||
class="dark-table"
|
||||
>
|
||||
<el-table-column type="index" width="60"></el-table-column>
|
||||
<el-table-column prop="clientWorkorder" label="工单号" width="130"></el-table-column>
|
||||
<el-table-column prop="blankNumber" label="毛坯号" width="120"></el-table-column>
|
||||
<el-table-column prop="finishedPartNumber" label="成品零件号" width="150"></el-table-column>
|
||||
<el-table-column prop="productDescription" label="产品描述"></el-table-column>
|
||||
<el-table-column prop="colour" label="颜色" width="100"></el-table-column>
|
||||
<el-table-column prop="specifications" label="规格" width="100"></el-table-column>
|
||||
<el-table-column prop="vehicleNumber" label="车数" width="80"></el-table-column>
|
||||
<el-table-column prop="previousNumber" label="上件数" width="80"></el-table-column>
|
||||
<el-table-column prop="remark3" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="small" v-if="scope.row.status === 2" type="success">已完成</el-tag>
|
||||
<el-tag size="small" v-else-if="scope.row.status === 1" type="warning">进行中</el-tag>
|
||||
<el-tag size="small" v-else type="info">未开始</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWorkOrderCarouselBoardData } from '@/api/kanbanManagement/carouselBoard.js'
|
||||
import * as echarts from 'echarts';
|
||||
import { DvTitle } from '@jiaminghi/data-view';
|
||||
import kbHeader from './kbHeader.vue';
|
||||
|
||||
|
||||
export default {
|
||||
name: 'WorkorderOnlineCard',
|
||||
components: {
|
||||
DvTitle
|
||||
kbHeader
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
workorderOnlineTable: [],
|
||||
tableHeight: '500px', // 调整表格高度,为图表留出空间
|
||||
realTotal: 0,
|
||||
tableHeight: '500px',
|
||||
workorderChart: null,
|
||||
productionChart: null,
|
||||
tableScrollTimer: null,
|
||||
refreshTimer: null,
|
||||
scrollCheckTimer: null,
|
||||
// 今日统计数据
|
||||
todayStatistics: {
|
||||
totalPlan: 0, // 今日总计划数
|
||||
completedPlan: 0, // 已完成计划数
|
||||
uncompletedPlan: 0, // 未完成计划数
|
||||
totalInput: 0, // 今日总投入数
|
||||
totalQualified: 0 // 今日总合格数
|
||||
totalInput: 0 // 今日总投入数
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('工单组件mounted生命周期调用')
|
||||
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(() => {
|
||||
this.getWorkorderData()
|
||||
}, 5 * 60 * 1000) // 5分钟 = 5 * 60 * 1000毫秒
|
||||
},
|
||||
|
||||
updated() {
|
||||
// 数据更新后初始化图表
|
||||
if (this.workorderOnlineTable.length > 0 && !this.workorderChart) {
|
||||
this.$nextTick(() => {
|
||||
// 数据更新后初始化图表和启动表格滚动
|
||||
this.$nextTick(() => {
|
||||
console.log('工单组件updated生命周期调用')
|
||||
if (this.workorderOnlineTable.length > 0) {
|
||||
this.initWorkorderChart()
|
||||
this.startTableAutoScroll()
|
||||
})
|
||||
}
|
||||
this.initProductionTypeChart()
|
||||
}
|
||||
// 无论数据是否为空,都尝试启动滚动,确保表格有内容时能正常滚动
|
||||
this.startTableAutoScroll()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
@@ -111,11 +170,25 @@ export default {
|
||||
this.workorderChart.dispose()
|
||||
this.workorderChart = null
|
||||
}
|
||||
// 清理表格滚动定时器
|
||||
if (this.productionChart) {
|
||||
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
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getWorkorderData() {
|
||||
@@ -130,10 +203,16 @@ export default {
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.workorderOnlineTable = res.data || []
|
||||
this.realTotal = this.workorderOnlineTable.length
|
||||
console.log('工单数据加载完成,共', this.workorderOnlineTable.length, '条记录')
|
||||
|
||||
// 计算今日统计数据
|
||||
this.calculateTodayStatistics()
|
||||
|
||||
// 数据加载完成后立即启动滚动
|
||||
this.$nextTick(() => {
|
||||
console.log('数据加载后启动滚动')
|
||||
this.startTableAutoScroll()
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -148,13 +227,12 @@ export default {
|
||||
totalPlan: 0,
|
||||
completedPlan: 0,
|
||||
uncompletedPlan: 0,
|
||||
totalInput: 0,
|
||||
totalQualified: 0
|
||||
totalInput: 0
|
||||
}
|
||||
|
||||
// 统计工单数据
|
||||
this.workorderOnlineTable.forEach(item => {
|
||||
// 假设vehicleNumber是计划数
|
||||
// 确保使用实际的计划数字段
|
||||
const planCount = Number(item.vehicleNumber) || 0
|
||||
this.todayStatistics.totalPlan += planCount
|
||||
|
||||
@@ -165,129 +243,274 @@ export default {
|
||||
this.todayStatistics.uncompletedPlan += planCount
|
||||
}
|
||||
|
||||
// 假设previousNumber是投入数
|
||||
// 确保使用实际的投入数字段
|
||||
this.todayStatistics.totalInput += Number(item.previousNumber) || 0
|
||||
|
||||
// 假设合格数为投入数的90%(实际应从API获取)
|
||||
const qualifiedCount = Math.round((Number(item.previousNumber) || 0) * 0.9)
|
||||
this.todayStatistics.totalQualified += qualifiedCount
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化工单状态分布环形进度图
|
||||
// 获取状态文本
|
||||
getStatusText(status) {
|
||||
switch (status) {
|
||||
case 2: return '已完成'
|
||||
case 1: return '进行中'
|
||||
default: return '未开始'
|
||||
}
|
||||
},
|
||||
|
||||
// 获取状态样式类
|
||||
getStatusClass(status) {
|
||||
switch (status) {
|
||||
case 2: return 'status-completed'
|
||||
case 1: return 'status-progress'
|
||||
default: return 'status-not-started'
|
||||
}
|
||||
},
|
||||
|
||||
// 初始化工单状态分布饼图
|
||||
initWorkorderChart() {
|
||||
const chartDom = document.getElementById('workorderStatusChart')
|
||||
if (!chartDom) return
|
||||
|
||||
this.workorderChart = echarts.init(chartDom)
|
||||
|
||||
// 准备环形图数据
|
||||
const statusCounts = {
|
||||
'已完成': 0,
|
||||
'进行中': 0,
|
||||
'未开始': 0,
|
||||
'已暂停': 0
|
||||
if (this.workorderChart) {
|
||||
this.workorderChart.dispose()
|
||||
}
|
||||
|
||||
// 根据工单状态统计数量
|
||||
this.workorderOnlineTable.forEach(item => {
|
||||
switch (item.status) {
|
||||
case 2: // 已完成
|
||||
statusCounts['已完成']++
|
||||
break
|
||||
case 1: // 进行中
|
||||
statusCounts['进行中']++
|
||||
break
|
||||
default: // 未开始
|
||||
statusCounts['未开始']++
|
||||
}
|
||||
})
|
||||
this.workorderChart = echarts.init(chartDom)
|
||||
|
||||
// 统计已完成和未完成数量
|
||||
const completedCount = this.workorderOnlineTable.filter(item => item.status === 2).length
|
||||
const uncompletedCount = this.workorderOnlineTable.filter(item => item.status !== 2).length
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '工单状态分布',
|
||||
left: 'center',
|
||||
top: 0
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 0
|
||||
orient: 'vertical',
|
||||
left: 10,
|
||||
top: 'center',
|
||||
textStyle: {
|
||||
color: '#00ffff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '工单状态',
|
||||
name: '今日生产',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'], // 环形图
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderColor: 'rgba(0, 20, 40, 0.6)',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}\n{d}%'
|
||||
formatter: '{b}\n{d}%',
|
||||
color: '#ffffff'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '16',
|
||||
fontWeight: 'bold'
|
||||
fontWeight: 'bold',
|
||||
color: '#00ffff'
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: statusCounts['已完成'], name: '已完成', itemStyle: { color: '#67c23a' } },
|
||||
{ value: statusCounts['进行中'], name: '进行中', itemStyle: { color: '#409EFF' } },
|
||||
{ value: statusCounts['未开始'], name: '未开始', itemStyle: { color: '#909399' } }
|
||||
].filter(item => item.value > 0) // 过滤掉数量为0的状态
|
||||
{ value: completedCount, name: '已完成', itemStyle: { color: '#67c23a' } },
|
||||
{ value: uncompletedCount, name: '未完成', itemStyle: { color: '#e6a23c' } }
|
||||
].filter(item => item.value > 0)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.workorderChart.setOption(option)
|
||||
},
|
||||
|
||||
// 初始化生产统计横向柱状图
|
||||
initProductionTypeChart() {
|
||||
const chartDom = document.getElementById('productionTypeChart')
|
||||
if (!chartDom) return
|
||||
|
||||
// 监听窗口大小变化,调整图表大小
|
||||
window.addEventListener('resize', () => {
|
||||
if (this.workorderChart) {
|
||||
this.workorderChart.resize()
|
||||
if (this.productionChart) {
|
||||
this.productionChart.dispose()
|
||||
}
|
||||
|
||||
this.productionChart = echarts.init(chartDom)
|
||||
|
||||
// 按零件汇总并统计上件数
|
||||
const partStats = {}
|
||||
this.workorderOnlineTable.forEach(item => {
|
||||
// 使用成品零件号作为唯一标识
|
||||
const partKey = item.finishedPartNumber || item.productDescription || '未知'
|
||||
const previousNumber = Number(item.previousNumber) || 0
|
||||
|
||||
if (!partStats[partKey]) {
|
||||
partStats[partKey] = {
|
||||
name: partKey,
|
||||
value: 0,
|
||||
description: item.productDescription || partKey
|
||||
}
|
||||
}
|
||||
partStats[partKey].value += previousNumber
|
||||
})
|
||||
|
||||
// 转换为数组并按上件数降序排序,确保数量最大的显示在顶部,只取前5个
|
||||
const sortedData = Object.values(partStats)
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.slice(0, 5)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '10%', // 减小左侧边距,让图表整体向左移动
|
||||
right: '10%',
|
||||
top: '10%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: 'rgba(255, 255, 255, 0.1)'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: sortedData.map(item => {
|
||||
// 使用描述或零件号,进行自动换行处理
|
||||
const displayText = item.description || item.name
|
||||
const maxLength = 8
|
||||
let result = displayText
|
||||
if (result.length > maxLength) {
|
||||
result = ''
|
||||
for (let i = 0; i < displayText.length; i += maxLength) {
|
||||
result += displayText.substring(i, i + maxLength) + '\n'
|
||||
}
|
||||
result = result.trim()
|
||||
}
|
||||
return result
|
||||
}),
|
||||
axisLabel: {
|
||||
color: '#00ffff',
|
||||
fontSize: 10,
|
||||
interval: 0
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '数量',
|
||||
type: 'bar',
|
||||
data: sortedData.map(item => item.value),
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#0066ff' },
|
||||
{ offset: 1, color: '#00ffff' }
|
||||
])
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
color: '#ffffff'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.productionChart.setOption(option)
|
||||
},
|
||||
|
||||
// 表格自动滚动功能 - 修复滚动逻辑
|
||||
startTableAutoScroll() {
|
||||
// 先清理可能存在的定时器
|
||||
if (this.tableScrollTimer) {
|
||||
cancelAnimationFrame(this.tableScrollTimer)
|
||||
this.tableScrollTimer = null
|
||||
}
|
||||
|
||||
// 确保表格元素已渲染
|
||||
this.$nextTick(() => {
|
||||
// 直接获取DOM元素,避免选择器问题
|
||||
const tableSection = this.$el.querySelector('.table-section')
|
||||
const wrapper = tableSection ? tableSection.querySelector('.custom-table-wrapper') : null
|
||||
console.log('工单组件滚动容器:', wrapper)
|
||||
if (!wrapper) {
|
||||
console.log('滚动容器未找到')
|
||||
return
|
||||
}
|
||||
|
||||
// 重要:确保容器内容高度大于容器高度才需要滚动
|
||||
const scrollableHeight = wrapper.scrollHeight - wrapper.clientHeight
|
||||
console.log('容器可滚动高度差:', scrollableHeight)
|
||||
|
||||
// 优化滚动容器样式设置
|
||||
// 不设置height,因为模板中已经通过:style绑定
|
||||
wrapper.style.overflow = 'auto'
|
||||
wrapper.style.overflowX = 'hidden'
|
||||
wrapper.style.scrollBehavior = 'smooth'
|
||||
console.log('设置滚动容器样式完成')
|
||||
|
||||
// 重置滚动位置
|
||||
wrapper.scrollTop = 0
|
||||
|
||||
// 增加滚动速度,使其更容易观察
|
||||
const scrollSpeed = 2 // 提高到2,确保明显可见
|
||||
|
||||
// 使用setInterval替代requestAnimationFrame,更稳定
|
||||
this.tableScrollTimer = setInterval(() => {
|
||||
if (wrapper && wrapper.scrollHeight > wrapper.clientHeight) {
|
||||
// 检查是否滚动到底部
|
||||
if (wrapper.scrollTop + 10 >= wrapper.scrollHeight - wrapper.clientHeight) {
|
||||
console.log('工单组件已滚动到底部,准备复位')
|
||||
// 重置到顶部
|
||||
wrapper.scrollTop = 0
|
||||
} else {
|
||||
// 持续滚动
|
||||
wrapper.scrollTop += scrollSpeed
|
||||
}
|
||||
}
|
||||
}, 50) // 每50毫秒滚动一次,更平滑
|
||||
|
||||
console.log('工单组件启动滚动定时器')
|
||||
})
|
||||
},
|
||||
|
||||
// 表格自动滚动功能
|
||||
startTableAutoScroll() {
|
||||
if (this.tableScrollTimer) {
|
||||
clearInterval(this.tableScrollTimer)
|
||||
handleResize() {
|
||||
// 考虑kbHeader的高度,重新计算内容高度
|
||||
const windowHeight = window.innerHeight
|
||||
// 减去标题高度和padding等,确保内容适配
|
||||
const availableHeight = windowHeight - 60 // 标题高度
|
||||
|
||||
// 调整第一行高度,进一步减小以给第二行留出更多空间
|
||||
const firstRow = document.querySelector('.first-row')
|
||||
if (firstRow) {
|
||||
firstRow.style.height = '200px' // 进一步减小第一行高度
|
||||
}
|
||||
|
||||
this.tableScrollTimer = setInterval(() => {
|
||||
if (this.$refs.workorderTable) {
|
||||
const elTableBody = this.$refs.workorderTable.$el.querySelector('.el-table__body-wrapper')
|
||||
if (elTableBody) {
|
||||
// 每次滚动一行的高度
|
||||
const scrollStep = 31 // 行高 + 边框
|
||||
elTableBody.scrollTop += scrollStep
|
||||
|
||||
// 如果滚动到底部,回到顶部继续滚动
|
||||
if (elTableBody.scrollTop + elTableBody.clientHeight >= elTableBody.scrollHeight) {
|
||||
elTableBody.scrollTop = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 2000) // 每2秒滚动一次
|
||||
},
|
||||
handleResize() {
|
||||
const windowHeight = window.innerHeight
|
||||
this.tableHeight = Math.max(windowHeight * 0.5, 400) + 'px' // 为图表留出空间
|
||||
// 计算表格高度,确保表格固定高度且不会超出屏幕
|
||||
// 将表格高度固定为一个合理的值,确保在各种屏幕尺寸下都能正常显示
|
||||
this.tableHeight = Math.min(Math.max(availableHeight - 350, 300), 400) + 'px' // 限制最大高度为400px
|
||||
|
||||
// 调整图表大小
|
||||
if (this.workorderChart) {
|
||||
this.workorderChart.resize()
|
||||
}
|
||||
if (this.productionChart) {
|
||||
this.productionChart.resize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,166 +518,244 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.workorder-online-card {
|
||||
padding: 15px;
|
||||
background: white;
|
||||
padding: 10px;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
min-height: 100vh; /* 确保至少填满整个视口高度 */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px; /* 减小间距 */
|
||||
}
|
||||
|
||||
/* kbHeader组件已包含标题样式 */
|
||||
|
||||
/* 第一行布局 */
|
||||
.first-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 15px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
/* 第二行布局 */
|
||||
.second-row {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 15px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 通用区域样式 */
|
||||
.chart-section,
|
||||
.statistics-section,
|
||||
.table-section,
|
||||
.bar-chart-section {
|
||||
background: rgba(0, 20, 40, 0.6);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
height: 100%;
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 今日统计数据样式 */
|
||||
.statistics-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f0f2f5;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
.section-title {
|
||||
color: #00ffff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.stat-value.completed {
|
||||
/* 统计翻牌器样式 */
|
||||
.flip-card-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15px;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.stat-value.uncompleted {
|
||||
.flip-card-value.uncompleted {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.stat-value.qualified {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
margin-bottom: 15px;
|
||||
flex-shrink: 0;
|
||||
background-color: #fafafa;
|
||||
|
||||
/* 自定义表格样式 */
|
||||
.custom-table-wrapper {
|
||||
/* 确保表格固定高度,不依赖flex:1,避免内容超出屏幕 */
|
||||
overflow-y: auto;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
background: rgba(0, 60, 120, 0.8);
|
||||
/* 确保高度已通过JS设置,这里不再设置 */
|
||||
}
|
||||
|
||||
.el-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
.custom-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 优化表格样式 */
|
||||
.el-table__header-wrapper th {
|
||||
background-color: #e6f7ff !important;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
.custom-table thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.custom-table 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;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
.el-table__body-wrapper td {
|
||||
.custom-table td {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
padding: 10px 5px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.table-row:hover td {
|
||||
background-color: rgba(0, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 状态标签样式 */
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
background-color: rgba(103, 194, 58, 0.3);
|
||||
border-color: #67c23a;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.status-progress {
|
||||
background-color: rgba(230, 162, 60, 0.3);
|
||||
border-color: #e6a23c;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.status-not-started {
|
||||
background-color: rgba(90, 143, 242, 0.3);
|
||||
border-color: #5a8ff2;
|
||||
color: #5a8ff2;
|
||||
}
|
||||
|
||||
/* 加载样式 */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* 优化斑马纹效果 */
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td {
|
||||
background-color: #fafafa;
|
||||
/* 滚动条样式 */
|
||||
.custom-table-wrapper::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
/* 高亮当前行 */
|
||||
.el-table__body tr:hover>td {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.workorder-online-card {
|
||||
padding: 20px;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.custom-table-wrapper::-webkit-scrollbar-track {
|
||||
background: rgba(0, 60, 120, 0.5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
.dark-table {
|
||||
background: rgba(0, 20, 40, 0.6) !important;
|
||||
.custom-table-wrapper::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 255, 255, 0.5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::v-deep .el-table__header th {
|
||||
background: rgba(0, 60, 120, 0.8) !important;
|
||||
color: #00ffff !important;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.3) !important;
|
||||
font-weight: bold;
|
||||
.custom-table-wrapper::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
::v-deep .el-table__body td {
|
||||
color: #ffffff !important;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
::v-deep .el-table__row:hover td {
|
||||
background-color: rgba(0, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
/* 标签样式优化 */
|
||||
::v-deep .el-tag {
|
||||
background-color: rgba(0, 102, 255, 0.3) !important;
|
||||
color: #ffffff !important;
|
||||
border-color: #0066ff !important;
|
||||
}
|
||||
|
||||
::v-deep .el-tag--success {
|
||||
background-color: rgba(103, 194, 58, 0.3) !important;
|
||||
border-color: #67c23a !important;
|
||||
}
|
||||
|
||||
::v-deep .el-tag--warning {
|
||||
background-color: rgba(230, 162, 60, 0.3) !important;
|
||||
border-color: #e6a23c !important;
|
||||
}
|
||||
|
||||
::v-deep .el-tag--info {
|
||||
background-color: rgba(90, 143, 242, 0.3) !important;
|
||||
border-color: #5a8ff2 !important;
|
||||
}
|
||||
|
||||
/* 加载动画样式 */
|
||||
::v-deep .el-loading-spinner .path {
|
||||
stroke: #00ffff !important;
|
||||
}
|
||||
|
||||
::v-deep .el-loading-spinner .el-loading-text {
|
||||
color: #00ffff !important;
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 1200px) {
|
||||
.first-row {
|
||||
grid-template-columns: 1fr;
|
||||
height: auto;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.second-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.flip-card-container {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="header">
|
||||
<span class="title"><slot></slot></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script></script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
background-image: url('../images/header.png');
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.title {
|
||||
font-size: 36px;
|
||||
font-weight: 600;
|
||||
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
||||
-webkit-background-clip: text; /* 仅将背景应用于文本 */
|
||||
-webkit-text-fill-color: transparent; /* 设置文本颜色为透明 */
|
||||
color: transparent; /* 兼容非WebKit浏览器 */
|
||||
background-image: linear-gradient(92deg, #0072ff 0%, #00eaff 50%, #01aaff 100%);
|
||||
}
|
||||
</style>
|
||||
BIN
src/views/kanbanManagement/CarouselBoard/images/header.png
Normal file
BIN
src/views/kanbanManagement/CarouselBoard/images/header.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
16
src/views/kanbanManagement/CarouselBoard/images/header.svg
Normal file
16
src/views/kanbanManagement/CarouselBoard/images/header.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100" viewBox="0 0 1200 100">
|
||||
<defs>
|
||||
<linearGradient id="headerGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#002854;stop-opacity:0.8" />
|
||||
<stop offset="50%" style="stop-color:#004d8c;stop-opacity:0.8" />
|
||||
<stop offset="100%" style="stop-color:#002854;stop-opacity:0.8" />
|
||||
</linearGradient>
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="5" result="blur" />
|
||||
<feComposite in="SourceGraphic" in2="blur" operator="over" />
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#headerGradient)" />
|
||||
<rect x="10" y="10" width="1180" height="80" rx="5" ry="5" fill="none" stroke="#00ffff" stroke-width="1" stroke-opacity="0.3" filter="url(#glow)" />
|
||||
<line x1="10" y1="90" x2="1190" y2="90" stroke="#00ffff" stroke-width="1" stroke-opacity="0.5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 953 B |
@@ -1,10 +1,8 @@
|
||||
<template>
|
||||
<dv-full-screen-container>
|
||||
<!-- <dv-decoration-1 :color="['#00ffff', '#0066ff']" />
|
||||
<dv-decoration-2 :color="['#00ffff', '#0066ff']" /> -->
|
||||
<div class="carousel-board-container">
|
||||
<dv-border-box-1 class="dashboard-border">
|
||||
<el-carousel ref="carousel" :interval="60000" height="99vh" arrow="never" indicator-position="bottom" autoplay
|
||||
<el-carousel ref="carousel" :interval="60000" height="100vh" arrow="never" indicator-position="bottom" autoplay
|
||||
class="dashboard-carousel">
|
||||
<el-carousel-item>
|
||||
<QualityStatisticsCard />
|
||||
@@ -53,9 +51,9 @@ export default {
|
||||
.carousel-board-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
padding: 0px;
|
||||
background: linear-gradient(135deg, #001529 0%, #002140 100%);
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -71,8 +69,9 @@ export default {
|
||||
|
||||
/* 优化轮播项样式 */
|
||||
.el-carousel__item {
|
||||
padding: 10px;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 确保指示器清晰可见 */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="carousel-board-management">
|
||||
<div class="carousel-board-root">
|
||||
<div class="carousel-board-management">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h3>轮播看板管理</h3>
|
||||
@@ -82,9 +83,9 @@
|
||||
<el-table-column prop="result" label="操作结果"></el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 时间选择弹窗 -->
|
||||
<!-- 时间选择弹窗 -->
|
||||
<el-dialog :title="`${currentAction}${currentModuleName}数据 - 选择日期`" :visible.sync="timeDialogVisible" width="400px" :close-on-click-modal="false">
|
||||
<div style="padding: 20px 0">
|
||||
<el-form label-position="top">
|
||||
@@ -97,7 +98,8 @@
|
||||
<el-button @click="timeDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmTimeSelection">{{ currentAction }}</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user