Browse Source

样式

master
han\hanst 4 days ago
parent
commit
9aed38f4f6
  1. 119
      src/App.vue
  2. 27
      src/utils/screenAdapt.js
  3. 43
      src/views/modules/longtron/production-plan-home-order.vue
  4. 167
      src/views/modules/longtron/screen-cable-cop-progress.vue
  5. 194
      src/views/modules/longtron/screen-factory-overview.vue
  6. 131
      src/views/modules/longtron/screen-machining-progress.vue
  7. 391
      src/views/modules/longtron/screen-renovation-progress.vue
  8. 41
      src/views/modules/longtron/screen-whole-lift-progress.vue

119
src/App.vue

@ -1,17 +1,24 @@
<template>
<div id="app">
<!-- 主内容区域 -->
<transition name="fade">
<div id="app" :class="{ 'screen-route-app': isScreenRoute }">
<div v-if="isScreenRoute" class="screen-route-shell">
<div class="screen-route-scale" :style="screenScaleStyle">
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
<transition v-else name="fade" mode="out-in">
<router-view></router-view>
</transition>
<!-- 全局审批通知管理器 -->
<approval-notification-manager ref="approvalNotificationManager"></approval-notification-manager>
<approval-notification-manager v-if="!isScreenRoute" ref="approvalNotificationManager"></approval-notification-manager>
</div>
</template>
<script>
import ApprovalNotificationManager from '@/components/ApprovalNotificationManager.vue'
import { SCREEN_DESIGN_HEIGHT, SCREEN_DESIGN_WIDTH, setScreenScale } from '@/utils/screenAdapt'
export default {
name: 'App',
@ -23,14 +30,49 @@ export default {
data() {
return {
query: {},
version: '1.3.3'
version: '1.3.3',
isScreenRoute: false,
screenScale: 1
}
},
computed: {
screenScaleStyle() {
const scale = this.screenScale > 0 ? this.screenScale : 1
const percent = `${100 / scale}%`
return {
width: percent,
height: percent,
transform: `scale(${scale})`,
transformOrigin: 'left top'
}
}
},
watch: {
'$route.path': {
immediate: true,
handler() {
this.syncScreenRouteMode()
}
}
},
created () {
this.versionReload()
},
mounted() {
window.addEventListener('resize', this.handleWindowResize)
this.syncScreenRouteMode()
},
beforeDestroy() {
window.removeEventListener('resize', this.handleWindowResize)
document.body.classList.remove('screen-route-body')
setScreenScale(1)
},
methods: {
/**
* 版本检查及自动刷新
@ -46,6 +88,38 @@ export default {
location.reload();
}
},
isScreenPath(path) {
return /^\/screen-/.test(String(path || ''))
},
syncScreenRouteMode() {
this.isScreenRoute = this.isScreenPath(this.$route && this.$route.path)
this.updateScreenBodyClass()
this.updateScreenScale()
},
updateScreenBodyClass() {
if (this.isScreenRoute) {
document.body.classList.add('screen-route-body')
} else {
document.body.classList.remove('screen-route-body')
}
},
handleWindowResize() {
this.updateScreenScale()
},
updateScreenScale() {
if (!this.isScreenRoute) {
this.screenScale = 1
setScreenScale(1)
return
}
const width = window.innerWidth || document.documentElement.clientWidth || SCREEN_DESIGN_WIDTH
const height = window.innerHeight || document.documentElement.clientHeight || SCREEN_DESIGN_HEIGHT
const scaleX = width / SCREEN_DESIGN_WIDTH
const scaleY = height / SCREEN_DESIGN_HEIGHT
const scale = Math.min(scaleX, scaleY)
this.screenScale = scale > 0 ? Number(scale.toFixed(6)) : 1
setScreenScale(this.screenScale)
},
/**
* 手动触发审批通知检查供外部调用
@ -70,5 +144,32 @@ export default {
</script>
<style>
/* 全局样式 */
body.screen-route-body {
overflow: hidden;
background: #000;
}
#app.screen-route-app {
width: 100vw;
height: 100vh;
overflow: hidden;
}
.screen-route-shell {
width: 100%;
height: 100%;
overflow: hidden;
}
.screen-route-scale {
width: 100%;
height: 100%;
overflow: hidden;
will-change: transform;
}
.screen-route-scale > * {
width: 100% !important;
height: 100% !important;
}
</style>

27
src/utils/screenAdapt.js

@ -0,0 +1,27 @@
export const SCREEN_DESIGN_WIDTH = 1920
export const SCREEN_DESIGN_HEIGHT = 1080
const SCREEN_SCALE_KEY = '__LONGCHUANG_SCREEN_SCALE__'
export function setScreenScale(scale) {
if (typeof window === 'undefined') return
const safeScale = typeof scale === 'number' && scale > 0 ? scale : 1
window[SCREEN_SCALE_KEY] = safeScale
}
export function getScreenScale() {
if (typeof window === 'undefined') return 1
const val = window[SCREEN_SCALE_KEY]
return typeof val === 'number' && val > 0 ? val : 1
}
export function getScreenInnerHeight() {
if (typeof window === 'undefined') return SCREEN_DESIGN_HEIGHT
const scale = getScreenScale()
return Math.max(0, Math.floor(window.innerHeight / scale))
}
export function getScreenInnerWidth() {
if (typeof window === 'undefined') return SCREEN_DESIGN_WIDTH
const scale = getScreenScale()
return Math.max(0, Math.floor(window.innerWidth / scale))
}

43
src/views/modules/longtron/production-plan-home-order.vue

@ -30,6 +30,13 @@
<el-button @click="getDataList('Y')" plain class="search-btn">查询</el-button>
<el-button @click="resetQuery()" plain class="reset-btn">重置</el-button>
<el-button @click="openEditDialog()" plain class="add-btn">新增订单</el-button>
<el-button
@click="finishOrder()"
plain
class="search-btn"
:disabled="!selectedOrder.orderNo || selectedOrder.status === '已完成'">
完工
</el-button>
</el-form-item>
</el-form>
@ -43,6 +50,13 @@
v-loading="dataListLoading"
style="width: 100%"
@current-change="onCurrentRowChange">
<el-table-column label="操作" width="180" align="center" >
<template slot-scope="scope">
<a type="text" @click="openEditDialog(scope.row)" v-if="scope.row.status === '已排产'">修改</a>
<a type="text" @click="openAssignDialog(scope.row)" v-if="scope.row.status !== '已完成' && scope.row.currentNode!=='全部完成'">分配人员</a>
<a type="text" style="color:#F56C6C" @click="deleteOrder(scope.row)" v-if="scope.row.status !== '已完成'">删除</a>
</template>
</el-table-column>
<el-table-column type="index" label="#" width="50" align="center"></el-table-column>
<el-table-column prop="projectNo" label="项目号" width="120" align="center"></el-table-column>
<el-table-column prop="modelNo" label="型号" width="130" align="center"></el-table-column>
@ -65,14 +79,6 @@
<el-table-column prop="finishDate" label="完工日期" width="120" align="center">
<template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template>
</el-table-column>
<el-table-column label="操作" width="300" align="center" >
<template slot-scope="scope">
<a type="text" @click="openEditDialog(scope.row)" v-if="scope.row.status === '已排产'">修改</a>
<a type="text" @click="openAssignDialog(scope.row)" v-if="scope.row.status !== '已完成' && scope.row.currentNode!=='全部完成'">分配人员</a>
<a type="text" @click="finishOrder(scope.row)" v-if="scope.row.status !== '已完成'">完工</a>
<a type="text" style="color:#F56C6C" @click="deleteOrder(scope.row)" v-if="scope.row.status !== '已完成'">删除</a>
</template>
</el-table-column>
</el-table>
<el-pagination
@ -766,17 +772,26 @@ export default {
row.finishDate = ''
},
finishOrder(row) {
finishHomeLiftOrder({ orderNo: row.orderNo }).then(({data}) => {
const targetRow = row || this.selectedOrder
if (!targetRow || !targetRow.orderNo) {
this.$message.warning('请先选择一条订单记录')
return
}
if (targetRow.status === '已完成') {
this.$message.warning('该订单已完工')
return
}
finishHomeLiftOrder({ orderNo: targetRow.orderNo }).then(({data}) => {
if (data && data.code === 0) {
this.$message.success(data.msg || '完工成功')
this.getDataList()
} else this.$message.error(data.msg || '完工失败')
}).catch(() => {
row.status = '已完成'
row.finishDate = this.dayjs().format('YYYY-MM-DD')
row.nodeList = row.nodeList.map(item => ({ ...item, status: '已完成' }))
row.currentNode = '全部完成'
row.nodeDoneCount = row.nodeTotalCount
targetRow.status = '已完成'
targetRow.finishDate = this.dayjs().format('YYYY-MM-DD')
targetRow.nodeList = targetRow.nodeList.map(item => ({ ...item, status: '已完成' }))
targetRow.currentNode = '全部完成'
targetRow.nodeDoneCount = targetRow.nodeTotalCount
this.$message.success('后端未完成,已在前端演示完工')
})
},

167
src/views/modules/longtron/screen-cable-cop-progress.vue

@ -1,166 +1,5 @@
<template>
<div class="screen-wrap">
<div class="top-bar">
<div class="header-left"><img class="site-navbar__brand-logo" src="~@/assets/img/lc.png" alt="龙闯电梯"></div>
<div class="header-center">
<div class="title">生产进度管理看板 · 线缆/COP自制</div>
</div>
<div class="tools">
<span class="time">{{ currentTime }}</span>
</div>
</div>
<script>
import ScreenRenovationProgress from './screen-renovation-progress'
<div class="kpi-row">
<div class="kpi-card"><div class="kpi-label">排产任务</div><div class="kpi-value">{{ kpi.total }}</div></div>
<div class="kpi-card"><div class="kpi-label">进行中</div><div class="kpi-value warning">{{ kpi.processing }}</div></div>
<div class="kpi-card"><div class="kpi-label">已完成</div><div class="kpi-value success">{{ kpi.finished }}</div></div>
<div class="kpi-card"><div class="kpi-label">完工达成率</div><div class="kpi-value highlight">{{ kpi.finishRate }}%</div></div>
</div>
<div class="legend-row">
<span class="legend-item"><i class="dot done"></i>已完成</span>
<span class="legend-item"><i class="dot todo"></i>未开始</span>
</div>
<el-table class="board-table" :data="boardList" :height="tableHeight" border stripe>
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="taskNo" label="任务单号" min-width="160" align="center"></el-table-column>
<el-table-column prop="sourceProjectNo" label="来源项目号" min-width="130" align="center"></el-table-column>
<el-table-column prop="taskType" label="类别" min-width="120" align="center">
<template slot-scope="scope">
<el-tag class="board-tag" :class="getTaskTypeClass(scope.row.taskType)" size="small">{{ scope.row.taskType }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="planFinishDate" label="计划完工日期" min-width="130" align="center"></el-table-column>
<el-table-column prop="taskQty" label="任务数量" min-width="90" align="center"></el-table-column>
<el-table-column prop="reportQty" label="已报工数量" min-width="100" align="center"></el-table-column>
<el-table-column label="线缆生产" width="110" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'lineProduction')"></span></template></el-table-column>
<el-table-column label="COP生产" width="110" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'copProduction')"></span></template></el-table-column>
<el-table-column prop="status" label="任务状态" width="100" align="center">
<template slot-scope="scope">
<el-tag class="board-tag" :class="getTaskStatusClass(scope.row.status)" size="small">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="finishDate" label="完工时间" width="120" align="center"><template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template></el-table-column>
</el-table>
</div>
</template>
<script>
import { getCableCopTaskList } from '@/api/longchuang/productionPlan'
const NODE_TEMPLATE = ['lineProduction', 'copProduction']
export default {
name: 'ScreenCableCopProgress',
data() {
return { loading: false, boardTimerId: null,tableHeight: 640, currentTime: '', timerId: null, boardList: [], kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 } }
},
mounted() { this.setTableHeight(); this.loadBoardData(); this.updateTime(); this.timerId = setInterval(this.updateTime, 1000);
// 60
this.boardTimerId = setInterval(() => {
this.loadBoardData()
}, 60000)
window.addEventListener('resize', this.setTableHeight) },
beforeDestroy() { if (this.timerId) clearInterval(this.timerId);
if (this.boardTimerId) {
clearInterval(this.boardTimerId)
}
window.removeEventListener('resize', this.setTableHeight) },
methods: {
setTableHeight() {
this.tableHeight = Math.max(420, window.innerHeight - 240)
},
loadBoardData() {
if (this.loading) return
this.loading = true
let statusList = ['已排产', '进行中']
getCableCopTaskList({ page: 1, limit: 300 ,statusList: statusList}).then(({data}) => {
this.loading = false
const source = (data && data.code === 0 && data.page && data.page.list) ? data.page.list : this.getMockList()
this.boardList = this.buildBoardList(source)
this.buildKpi()
}).catch(() => { this.loading = false; this.boardList = this.buildBoardList(this.getMockList()); this.buildKpi() })
},
buildBoardList(sourceList) {
const statusAllow = ['已排产', '进行中', '已完成']
return sourceList.filter(item => statusAllow.includes(item.status)).map(item => ({ ...item, nodeStatusMap: this.toNodeStatusMap(item.nodeList) }))
},
toNodeStatusMap(nodeList) {
const map = {}
NODE_TEMPLATE.forEach(code => { map[code] = '未开始' })
;(nodeList || []).forEach(node => { map[node.nodeCode] = node.status || '未开始' })
return map
},
buildKpi() {
const total = this.boardList.length
const processing = this.boardList.filter(item => item.status === '进行中').length
const finished = this.boardList.filter(item => item.status === '已完成').length
this.kpi = { total, processing, finished, finishRate: total ? Math.round((finished / total) * 100) : 0 }
},
getNodeCellText(row, code) { return row.nodeStatusMap[code] || '未开始' },
getNodeCellClass(row, code) {
const status = this.getNodeCellText(row, code)
if (status === '已完成') return 'node-chip done'
return 'node-chip todo'
},
getTaskTypeClass(taskType) {
return taskType === '线缆自制' ? 'type-cable' : 'type-cop'
},
getTaskStatusClass(status) {
if (status === '已完成') return 'status-done'
if (status === '进行中') return 'status-doing'
return 'status-planned'
},
updateTime() {
const now = new Date()
const weekList = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const d = this.dayjs(now)
this.currentTime = `${d.format('YYYY/MM/DD')} ${weekList[now.getDay()]} ${d.format('HH:mm:ss')}`
},
getMockList() {
return []
}
}
}
export default ScreenRenovationProgress
</script>
<style>
.screen-wrap{height:100vh;padding:16px;background:linear-gradient(165deg,#0a1f36 0%,#0f2b47 50%,#0a2037 100%);display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}
.top-bar{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid rgba(109,167,219,.24);background:rgba(9,29,49,.82)}
.top-bar{position:relative}
.header-left{display:flex;align-items:center;min-width:110px;z-index:2}
.logo-box{width:86px;height:30px;border-radius:6px;border:1px solid rgba(128,198,255,.45);color:#d8edff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;background:rgba(36,82,122,.35)}
.header-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;max-width:70%;width:auto;padding:6px 24px 8px;border-radius:8px;border:1px solid rgba(96,170,232,.34);background:linear-gradient(180deg,rgba(33,73,116,.52),rgba(16,44,77,.42))}
.header-center::before{content:'';position:absolute;left:50%;transform:translateX(-50%);top:-8px;width:68%;height:1px;background:linear-gradient(90deg,rgba(87,164,230,0),rgba(87,164,230,.9),rgba(87,164,230,0))}
.top-bar .tools{margin-left:auto;z-index:2}
.title{color:#8fe7ff;font-size:30px;font-weight:800;letter-spacing:3px;line-height:1.05;text-shadow:0 0 14px rgba(79,179,255,.36)}.subtitle{margin-top:4px;color:#c7e8ff;font-size:12px;letter-spacing:1px}
.tools{display:flex;align-items:center;gap:12px}.time{color:#89d7ff;font-size:20px;font-weight:600}
.refresh-btn{background:#e9f3ff;border-color:#bad8f8;color:#2d7fc9}.refresh-btn:hover{background:#2d7fc9;border-color:#2d7fc9;color:#fff}
.kpi-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:12px 0 8px}
.kpi-card{border-radius:10px;padding:12px 14px;background:rgba(14,43,70,.82);border:1px solid rgba(101,157,209,.22)}
.kpi-label{color:#b7d3ee;font-size:13px}.kpi-value{margin-top:6px;font-size:30px;font-weight:700;color:#edf6ff}
.kpi-value.warning{color:#ffd166}.kpi-value.success{color:#74dfa3}.kpi-value.highlight{color:#79d5ff}
.legend-row{display:flex;gap:16px;margin:0 0 10px 2px}.legend-item{display:inline-flex;align-items:center;color:#c7dff4;font-size:12px}
.dot{width:10px;height:10px;border-radius:50%;margin-right:6px;display:inline-block}.dot.done{background:#69e4a4}.dot.doing{background:#ffd35d}.dot.todo{background:#dbe3ee}
.board-table{border-radius:10px;overflow:hidden;border:1px solid rgba(86,140,190,.35);flex:1;min-height:0}
.board-table .cell {
line-height: 30px;
font-size: 16px;
height: 30px;
}
.screen-wrap .board-table .el-table,.screen-wrap .board-table .el-table__expanded-cell,.screen-wrap .board-table .el-table__body-wrapper,.screen-wrap .board-table .el-table__empty-block,.screen-wrap .board-table .el-table__fixed-body-wrapper{background:rgba(12,39,64,.96)!important;color:#fff!important}
.screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:14px!important;padding:12px 0!important}
.screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:46px!important;vertical-align:middle!important}
.screen-wrap .board-table .el-table--striped .el-table__body tr.el-table__row--striped>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr.el-table__row--striped>td{background:rgba(29,66,102,.96)!important}
.screen-wrap .board-table .el-table--enable-row-hover .el-table__body tr:hover>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr:hover>td{background:rgba(39,81,123,.96)!important}
.screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:24px!important;overflow:visible!important}
.screen-wrap .board-table .el-table__empty-text{color:#9ac2e7!important}
.node-chip{display:inline-block!important;width:46px;height:14px;margin:0 auto;border-radius:2px}
.node-chip.done{background:#6de3a0}.node-chip.todo{background:#dce4ee}
.board-tag{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:66px!important;height:24px!important;padding:0 10px!important;border-radius:12px!important;border:1px solid transparent!important;font-size:12px!important;font-weight:600!important;line-height:22px!important;box-sizing:border-box!important}
.board-tag.type-cable{color:#73cfff!important;background:rgba(28,88,136,.32)!important;border-color:rgba(115,207,255,.45)!important}
.board-tag.type-cop{color:#7ef0c0!important;background:rgba(31,108,89,.32)!important;border-color:rgba(126,240,192,.45)!important}
.board-tag.status-planned{color:#b9c7d8!important;background:rgba(96,118,141,.28)!important;border-color:rgba(185,199,216,.34)!important}
.board-tag.status-doing{color:#ffd774!important;background:rgba(120,92,27,.32)!important;border-color:rgba(255,215,116,.45)!important}
.board-tag.status-done{color:#83f3be!important;background:rgba(31,110,84,.32)!important;border-color:rgba(131,243,190,.45)!important}
</style>

194
src/views/modules/longtron/screen-factory-overview.vue

@ -12,20 +12,20 @@
<div class="kpi-grid">
<div class="kpi-card">
<div class="kpi-label">近一月整梯排产</div>
<div class="kpi-value">{{ kpi.monthWhole }}</div>
<div class="kpi-label">整梯完工比</div>
<div class="kpi-value">{{ kpi.wholeFinishRate }}%</div>
</div>
<div class="kpi-card">
<div class="kpi-label">近一月VL2.5升级排产</div>
<div class="kpi-value">{{ kpi.monthRenovation }}</div>
<div class="kpi-label">VL2.5升级完工比</div>
<div class="kpi-value">{{ kpi.renovationFinishRate }}%</div>
</div>
<div class="kpi-card">
<div class="kpi-label">近一月线缆/COP任务</div>
<div class="kpi-value">{{ kpi.monthTask }}</div>
<div class="kpi-label">线缆/COP完工比</div>
<div class="kpi-value">{{ kpi.taskFinishRate }}%</div>
</div>
<div class="kpi-card">
<div class="kpi-label">近一月机加工任务</div>
<div class="kpi-value">{{ kpi.monthMachining }}</div>
<div class="kpi-label">机加工完工比</div>
<div class="kpi-value">{{ kpi.machiningFinishRate }}%</div>
</div>
<div class="kpi-card">
<div class="kpi-label">近一月完工达成率</div>
@ -43,7 +43,7 @@
<div class="chart-grid">
<div class="panel">
<div class="panel-title">近一月排产/完工对</div>
<div class="panel-title">全量排产完工</div>
<div id="overviewBarChart" class="chart-box"></div>
</div>
<div class="panel">
@ -92,10 +92,10 @@ export default {
renovationList: [],
machiningList: [],
kpi: {
monthWhole: 0,
monthRenovation: 0,
monthTask: 0,
monthMachining: 0,
wholeFinishRate: 0,
renovationFinishRate: 0,
taskFinishRate: 0,
machiningFinishRate: 0,
finishRate: 0,
wipTotal: 0,
onTimeRate: 0
@ -122,7 +122,7 @@ export default {
loadBoardData() {
if (this.loading) return
this.loading = true
getFactoryOverviewBoardData({ page: 1, limit: 500, statusList: STATUS_ALLOW_LIST }).then(({ data }) => {
getFactoryOverviewBoardData({ page: 1, limit: 100000, statusList: STATUS_ALLOW_LIST }).then(({ data }) => {
this.loading = false
const boardData = data && data.code === 0 && data.boardData ? data.boardData : {}
this.wholeList = this.normalizeBoardList(boardData.homeLiftList)
@ -148,10 +148,20 @@ export default {
return (sourceList || [])
.filter(item => STATUS_ALLOW_LIST.includes(item.status))
.map(item => ({
...item,
nodeList: item.nodeList || []
status: item.status || '',
planDeliveryDate: item.planDeliveryDate || '',
planFinishDate: item.planFinishDate || '',
createTime: item.createTime || '',
finishDate: item.finishDate || '',
nodeList: this.normalizeNodeList(item.nodeList)
}))
},
normalizeNodeList(sourceList) {
return (sourceList || []).map(node => ({
nodeName: node && node.nodeName ? node.nodeName : '',
status: node && node.status ? node.status : ''
}))
},
buildDashboard() {
const periodStart = this.dayjs().subtract(1, 'month').startOf('day')
const allOrderList = this.wholeList
@ -166,18 +176,18 @@ export default {
this.renovationList.filter(item => item.status === '进行中').length +
this.machiningList.filter(item => item.status === '进行中').length
const monthWhole = this.countByMonth(this.wholeList, periodStart)
const monthRenovation = this.countByMonth(this.renovationList, periodStart)
const monthTask = this.countByMonth(this.taskList, periodStart)
const monthMachining = this.countByMonth(this.machiningList, periodStart)
const wholeFinishRate = this.calcFinishRate(this.wholeList)
const renovationFinishRate = this.calcFinishRate(this.renovationList)
const taskFinishRate = this.calcFinishRate(this.taskList)
const machiningFinishRate = this.calcFinishRate(this.machiningList)
const onTimeTotal = allOrderList.filter(item => item.status === '已完成').length
const onTimeCount = allOrderList.filter(item => item.status === '已完成' && this.isOnTime(item)).length
this.kpi.monthWhole = monthWhole
this.kpi.monthRenovation = monthRenovation
this.kpi.monthTask = monthTask
this.kpi.monthMachining = monthMachining
this.kpi.wholeFinishRate = wholeFinishRate
this.kpi.renovationFinishRate = renovationFinishRate
this.kpi.taskFinishRate = taskFinishRate
this.kpi.machiningFinishRate = machiningFinishRate
this.kpi.finishRate = inScheduleAll ? Math.round((finishedAll / inScheduleAll) * 100) : 0
this.kpi.wipTotal = wipTotal
this.kpi.onTimeRate = onTimeTotal ? Math.round((onTimeCount / onTimeTotal) * 100) : 0
@ -250,56 +260,56 @@ export default {
if (rate >= 60) return 'is-mid'
return 'is-low'
},
calcFinishRate(list) {
const total = (list || []).length
if (!total) {
return 0
}
const finished = list.filter(item => item.status === '已完成').length
return Math.round((finished / total) * 100)
},
renderBarChart() {
const el = document.getElementById('overviewBarChart')
if (!el) return
if (!this.chartBar) this.chartBar = echarts.init(el)
const periodStart = this.dayjs().subtract(1, 'month').startOf('day')
const scheduledWhole = this.countByMonth(this.wholeList.filter(item => STATUS_ALLOW_LIST.includes(item.status)), periodStart)
const scheduledRenovation = this.countByMonth(this.renovationList.filter(item => STATUS_ALLOW_LIST.includes(item.status)), periodStart)
const scheduledTask = this.countByMonth(this.taskList.filter(item => STATUS_ALLOW_LIST.includes(item.status)), periodStart)
const scheduledMachining = this.countByMonth(this.machiningList.filter(item => STATUS_ALLOW_LIST.includes(item.status)), periodStart)
const finishedWhole = this.countFinishedInRange(this.wholeList, periodStart)
const finishedRenovation = this.countFinishedInRange(this.renovationList, periodStart)
const finishedTask = this.countFinishedInRange(this.taskList, periodStart)
const finishedMachining = this.countFinishedInRange(this.machiningList, periodStart)
const wholeRate = this.calcFinishRate(this.wholeList)
const renovationRate = this.calcFinishRate(this.renovationList)
const taskRate = this.calcFinishRate(this.taskList)
const machiningRate = this.calcFinishRate(this.machiningList)
this.chartBar.setOption({
color: ['#4fa8ff', '#6ed3a6'],
animation: false,
color: ['#6ed3a6'],
grid: { left: 30, right: 20, top: 40, bottom: 20, containLabel: true },
tooltip: { trigger: 'axis' },
legend: { textStyle: { color: '#cbe7ff' }, data: ['排产量', '完工量'] },
xAxis: { type: 'category', axisLabel: { color: '#cbe7ff' }, data: ['整梯', 'VL2.5升级', '线缆/COP', '机加工'] },
yAxis: { type: 'value', axisLabel: { color: '#cbe7ff' }, splitLine: { lineStyle: { color: 'rgba(160,200,240,0.15)' } } },
tooltip: {
trigger: 'axis',
formatter: (params) => {
const point = (params && params[0]) || {}
return `${point.axisValue}<br/>完工比:${point.value || 0}%`
}
},
legend: { textStyle: { color: '#cbe7ff', fontSize: 13 }, data: ['完工比'] },
xAxis: { type: 'category', axisLabel: { color: '#cbe7ff', fontSize: 13 }, data: ['整梯', 'VL2.5升级', '线缆/COP', '机加工'] },
yAxis: {
type: 'value',
min: 0,
max: 100,
axisLabel: { color: '#cbe7ff', fontSize: 13, formatter: '{value}%' },
splitLine: { lineStyle: { color: 'rgba(160,200,240,0.15)' } }
},
series: [
{
name: '排产量',
type: 'bar',
barWidth: 24,
data: [scheduledWhole, scheduledRenovation, scheduledTask, scheduledMachining],
itemStyle: { color: '#4fa8ff' },
label: {
normal: {
show: true,
position: 'top',
color: '#d4ebff',
fontSize: 12,
formatter: '{c}'
}
}
},
{
name: '完工量',
name: '完工比',
type: 'bar',
barWidth: 24,
data: [finishedWhole, finishedRenovation, finishedTask, finishedMachining],
barWidth: 28,
data: [wholeRate, renovationRate, taskRate, machiningRate],
itemStyle: { color: '#6ed3a6' },
label: {
normal: {
show: true,
position: 'top',
color: '#d4ebff',
fontSize: 12,
formatter: '{c}'
fontSize: 13,
formatter: '{c}%'
}
}
}
@ -321,12 +331,13 @@ export default {
{ value: machiningCount, name: '机加工任务' }
]
this.chartPie.setOption({
animation: false,
color: ['#57b8ff', '#5dd4b0', '#7fa6d9', '#f5c15e'],
tooltip: {
trigger: 'item',
formatter: (params) => `${params.name}<br/>数量:${params.value}<br/>占比:${params.percent || 0}%`
},
legend: { bottom: 2, textStyle: { color: '#cbe7ff' } },
legend: { bottom: 2, textStyle: { color: '#cbe7ff', fontSize: 13 } },
series: [{
type: 'pie',
radius: ['42%', '72%'],
@ -338,7 +349,7 @@ export default {
show: true,
position: 'outside',
color: '#d4ebff',
fontSize: 14,
fontSize: 15,
formatter: '{b} {d}%'
},
emphasis: {
@ -378,12 +389,13 @@ export default {
machiningSeries.push(this.countFinishByDay(this.machiningList, day))
}
this.chartLine.setOption({
animation: false,
color: ['#6cc7ff', '#74e0b3', '#7fa6d9', '#f5c15e'],
grid: { left: 35, right: 20, top: 34, bottom: 20, containLabel: true },
tooltip: { trigger: 'axis' },
legend: { textStyle: { color: '#cbe7ff' }, data: ['整梯完工', 'VL2.5升级完工', '线缆/COP完工', '机加工完工'] },
xAxis: { type: 'category', axisLabel: { color: '#cbe7ff' }, data: xAxis },
yAxis: { type: 'value', axisLabel: { color: '#cbe7ff' }, splitLine: { lineStyle: { color: 'rgba(160,200,240,0.15)' } } },
legend: { textStyle: { color: '#cbe7ff', fontSize: 13 }, data: ['整梯完工', 'VL2.5升级完工', '线缆/COP完工', '机加工完工'] },
xAxis: { type: 'category', axisLabel: { color: '#cbe7ff', fontSize: 13 }, data: xAxis },
yAxis: { type: 'value', axisLabel: { color: '#cbe7ff', fontSize: 13 }, splitLine: { lineStyle: { color: 'rgba(160,200,240,0.15)' } } },
series: [
{ name: '整梯完工', type: 'line', smooth: true, symbol: 'circle', symbolSize: 7, data: wholeSeries, itemStyle: { color: '#6cc7ff' }, lineStyle: { color: '#6cc7ff' }, areaStyle: { color: 'rgba(108,199,255,0.18)' } },
{ name: 'VL2.5升级完工', type: 'line', smooth: true, symbol: 'circle', symbolSize: 7, data: renovationSeries, itemStyle: { color: '#74e0b3' }, lineStyle: { color: '#74e0b3' }, areaStyle: { color: 'rgba(116,224,179,0.18)' } },
@ -455,7 +467,7 @@ export default {
border-radius: 6px;
border: 1px solid rgba(128, 198, 255, 0.45);
color: #d8edff;
font-size: 14px;
font-size: 13px;
font-weight: 700;
display: flex;
align-items: center;
@ -490,7 +502,7 @@ export default {
}
.header-title {
font-size: 30px;
font-size: 32px;
font-weight: 800;
letter-spacing: 3px;
line-height: 1.05;
@ -500,7 +512,7 @@ export default {
.header-subtitle {
margin-top: 4px;
font-size: 12px;
font-size: 11px;
color: #c7e8ff;
letter-spacing: 1px;
}
@ -514,7 +526,7 @@ export default {
}
.time-text {
font-size: 18px;
font-size: 20px;
font-weight: 600;
color: #7bd9ff;
}
@ -533,8 +545,9 @@ export default {
.kpi-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 10px;
grid-template-columns: repeat(7, minmax(0, 1fr));
gap: 14px;
padding: 0 4px;
margin-bottom: 14px;
}
@ -542,17 +555,21 @@ export default {
border-radius: 10px;
padding: 12px 14px;
background: linear-gradient(135deg, rgba(29, 66, 106, 0.95), rgba(17, 43, 69, 0.92));
border: 1px solid rgba(95, 180, 255, 0.24);
border: 1px solid rgba(95, 180, 255, 0.45);
box-shadow: inset 0 0 0 1px rgba(132, 210, 255, 0.12), 0 0 8px rgba(24, 96, 160, 0.24);
width: 96%;
justify-self: center;
box-sizing: border-box;
}
.kpi-label {
font-size: 12px;
font-size: 13px;
color: #b9d8f8;
}
.kpi-value {
margin-top: 6px;
font-size: 28px;
font-size: 31px;
font-weight: 700;
color: #dff0ff;
}
@ -573,9 +590,11 @@ export default {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
gap: 12px;
gap: 18px;
padding: 2px;
flex: 1;
min-height: 0;
box-sizing: border-box;
}
.panel {
@ -586,6 +605,7 @@ export default {
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
}
.panel.wide {
@ -593,7 +613,7 @@ export default {
}
.panel-title {
font-size: 14px;
font-size: 15px;
font-weight: 600;
color: #d9ebff;
margin-bottom: 8px;
@ -615,19 +635,27 @@ export default {
overflow: hidden;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
gap: 10px;
padding: 2px;
box-sizing: border-box;
}
.node-rate-card {
padding: 8px 10px;
border-radius: 8px;
border: 1px solid rgba(103, 168, 227, 0.25);
background: rgba(29, 58, 86, 0.5);
border: 1px solid rgba(103, 168, 227, 0.5);
background: rgba(29, 58, 86, 0.64);
box-shadow: inset 0 0 0 1px rgba(132, 210, 255, 0.1);
width: 98%;
justify-self: center;
box-sizing: border-box;
}
.node-rate-name {
color: #d1e7ff;
font-size: 12px;
font-size: 13px;
line-height: 20px;
padding-top: 1px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -642,15 +670,15 @@ export default {
.node-rate-count {
color: #9cc5ea;
font-size: 12px;
font-size: 13px;
}
.node-rate-badge {
min-width: 50px;
min-width: 60px;
text-align: center;
padding: 2px 8px;
padding: 2px 10px;
border-radius: 10px;
font-size: 12px;
font-size: 13px;
font-weight: 700;
}

131
src/views/modules/longtron/screen-machining-progress.vue

@ -1,132 +1,5 @@
<template>
<div class="screen-wrap">
<div class="top-bar">
<div class="header-left"><img class="site-navbar__brand-logo" src="~@/assets/img/lc.png" alt="龙闯电梯"></div>
<div class="header-center">
<div class="title">机加工生产进度看板</div>
</div>
<div class="tools">
<span class="time">{{ currentTime }}</span>
</div>
</div>
<div class="kpi-row">
<div class="kpi-card"><div class="kpi-label">排产任务</div><div class="kpi-value">{{ kpi.total }}</div></div>
<div class="kpi-card"><div class="kpi-label">进行中</div><div class="kpi-value warning">{{ kpi.processing }}</div></div>
<div class="kpi-card"><div class="kpi-label">已完成</div><div class="kpi-value success">{{ kpi.finished }}</div></div>
<div class="kpi-card"><div class="kpi-label">完工达成率</div><div class="kpi-value highlight">{{ kpi.finishRate }}%</div></div>
</div>
<el-table class="board-table" :data="boardList" :height="tableHeight" border stripe>
<el-table-column type="index" label="序号" width="70" align="center"></el-table-column>
<el-table-column prop="modelNo" label="物料号" min-width="180" align="center"></el-table-column>
<el-table-column prop="taskQty" label="计划数量" min-width="130" align="center"></el-table-column>
<el-table-column prop="reportQty" label="实际数量" min-width="130" align="center"></el-table-column>
<el-table-column prop="planFinishDate" label="计划完工日期" min-width="170" align="center"></el-table-column>
<el-table-column prop="finishDate" label="实际完工日期" min-width="170" align="center">
<template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getMachiningTaskList } from '@/api/longchuang/productionPlan'
import ScreenRenovationProgress from './screen-renovation-progress'
export default {
name: 'ScreenMachiningProgress',
data() {
return {
loading: false,
boardTimerId: null,
tableHeight: 640,
currentTime: '',
timerId: null,
boardList: [],
kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 }
}
},
mounted() {
this.setTableHeight()
this.loadBoardData()
this.updateTime()
this.timerId = setInterval(this.updateTime, 1000)
this.boardTimerId = setInterval(() => {
this.loadBoardData()
}, 60000)
window.addEventListener('resize', this.setTableHeight)
},
beforeDestroy() {
if (this.timerId) clearInterval(this.timerId)
if (this.boardTimerId) clearInterval(this.boardTimerId)
window.removeEventListener('resize', this.setTableHeight)
},
methods: {
setTableHeight() {
this.tableHeight = Math.max(420, window.innerHeight - 220)
},
loadBoardData() {
if (this.loading) return
this.loading = true
getMachiningTaskList({ page: 1, limit: 300, statusList: ['已排产', '进行中', '已完成'] }).then(({ data }) => {
this.loading = false
const source = (data && data.code === 0 && data.page && data.page.list) ? data.page.list : this.getMockList()
this.boardList = this.buildBoardList(source)
this.buildKpi()
}).catch(() => {
this.loading = false
this.boardList = this.buildBoardList(this.getMockList())
this.buildKpi()
})
},
buildBoardList(sourceList) {
const statusAllow = ['已排产', '进行中', '已完成']
return (sourceList || []).filter(item => statusAllow.includes(item.status)).map(item => ({
...item,
taskQty: item.taskQty == null ? 0 : item.taskQty,
reportQty: item.reportQty == null ? 0 : item.reportQty
}))
},
buildKpi() {
const total = this.boardList.length
const processing = this.boardList.filter(item => item.status === '进行中').length
const finished = this.boardList.filter(item => item.status === '已完成').length
this.kpi = { total, processing, finished, finishRate: total ? Math.round((finished / total) * 100) : 0 }
},
updateTime() {
const now = new Date()
const weekList = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const d = this.dayjs(now)
this.currentTime = `${d.format('YYYY/MM/DD')} ${weekList[now.getDay()]} ${d.format('HH:mm:ss')}`
},
getMockList() {
return []
}
}
}
export default ScreenRenovationProgress
</script>
<style>
.screen-wrap{height:100vh;padding:16px;background:linear-gradient(165deg,#0a1f36 0%,#0f2b47 50%,#0a2037 100%);display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}
.top-bar{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid rgba(109,167,219,.24);background:rgba(9,29,49,.82)}
.top-bar{position:relative}
.header-left{display:flex;align-items:center;min-width:110px;z-index:2}
.header-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;max-width:70%;width:auto;padding:6px 24px 8px;border-radius:8px;border:1px solid rgba(96,170,232,.34);background:linear-gradient(180deg,rgba(33,73,116,.52),rgba(16,44,77,.42))}
.header-center::before{content:'';position:absolute;left:50%;transform:translateX(-50%);top:-8px;width:68%;height:1px;background:linear-gradient(90deg,rgba(87,164,230,0),rgba(87,164,230,.9),rgba(87,164,230,0))}
.top-bar .tools{margin-left:auto;z-index:2}
.title{color:#8fe7ff;font-size:30px;font-weight:800;letter-spacing:3px;line-height:1.05;text-shadow:0 0 14px rgba(79,179,255,.36)}
.tools{display:flex;align-items:center;gap:12px}.time{color:#89d7ff;font-size:20px;font-weight:600}
.kpi-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:12px 0 10px}
.kpi-card{border-radius:10px;padding:12px 14px;background:rgba(14,43,70,.82);border:1px solid rgba(101,157,209,.22)}
.kpi-label{color:#b7d3ee;font-size:13px}.kpi-value{margin-top:6px;font-size:30px;font-weight:700;color:#edf6ff}
.kpi-value.warning{color:#ffd166}.kpi-value.success{color:#74dfa3}.kpi-value.highlight{color:#79d5ff}
.board-table{border-radius:10px;overflow:hidden;border:1px solid rgba(86,140,190,.35);flex:1;min-height:0}
.board-table .cell{line-height:30px;font-size:16px;height:30px}
.screen-wrap .board-table .el-table,.screen-wrap .board-table .el-table__expanded-cell,.screen-wrap .board-table .el-table__body-wrapper,.screen-wrap .board-table .el-table__empty-block,.screen-wrap .board-table .el-table__fixed-body-wrapper{background:rgba(12,39,64,.96)!important;color:#fff!important}
.screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:14px!important;padding:12px 0!important}
.screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:46px!important;vertical-align:middle!important}
.screen-wrap .board-table .el-table--striped .el-table__body tr.el-table__row--striped>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr.el-table__row--striped>td{background:rgba(29,66,102,.96)!important}
.screen-wrap .board-table .el-table--enable-row-hover .el-table__body tr:hover>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr:hover>td{background:rgba(39,81,123,.96)!important}
.screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:24px!important;overflow:visible!important}
.screen-wrap .board-table .el-table__empty-text{color:#9ac2e7!important}
</style>

391
src/views/modules/longtron/screen-renovation-progress.vue

@ -3,18 +3,38 @@
<div class="top-bar">
<div class="header-left"><img class="site-navbar__brand-logo" src="~@/assets/img/lc.png" alt="龙闯电梯"></div>
<div class="header-center">
<div class="title">生产进度管理看板 · VL2.5升级</div>
<div class="title">生产进度管理看板 · VL2.5升级/线缆COP/机加工</div>
</div>
<div class="tools">
<span class="time">{{ currentTime }}</span>
</div>
</div>
<div class="kpi-row">
<div class="kpi-card"><div class="kpi-label">排产订单</div><div class="kpi-value">{{ kpi.total }}</div></div>
<div class="kpi-card"><div class="kpi-label">进行中</div><div class="kpi-value warning">{{ kpi.processing }}</div></div>
<div class="kpi-card"><div class="kpi-label">已完成</div><div class="kpi-value success">{{ kpi.finished }}</div></div>
<div class="kpi-card"><div class="kpi-label">完工达成率</div><div class="kpi-value highlight">{{ kpi.finishRate }}%</div></div>
<div class="summary-row">
<div class="summary-card">
<div class="summary-title">VL2.5升级订单</div>
<div class="summary-main">
<span class="summary-rate">{{ renovationKpi.finishRate }}%</span>
<span class="summary-rate-label">完工达成率</span>
</div>
<div class="summary-meta">排产 {{ renovationKpi.total }} · 进行中 {{ renovationKpi.processing }} · 已完成 {{ renovationKpi.finished }}</div>
</div>
<div class="summary-card">
<div class="summary-title">线缆/COP任务</div>
<div class="summary-main">
<span class="summary-rate">{{ cableCopKpi.finishRate }}%</span>
<span class="summary-rate-label">完工达成率</span>
</div>
<div class="summary-meta">排产 {{ cableCopKpi.total }} · 进行中 {{ cableCopKpi.processing }} · 已完成 {{ cableCopKpi.finished }}</div>
</div>
<div class="summary-card">
<div class="summary-title">机加工任务</div>
<div class="summary-main">
<span class="summary-rate">{{ machiningKpi.finishRate }}%</span>
<span class="summary-rate-label">完工达成率</span>
</div>
<div class="summary-meta">排产 {{ machiningKpi.total }} · 进行中 {{ machiningKpi.processing }} · 已完成 {{ machiningKpi.finished }}</div>
</div>
</div>
<div class="legend-row">
@ -22,79 +42,291 @@
<span class="legend-item"><i class="dot todo"></i>未开始</span>
</div>
<el-table class="board-table" :data="boardList" :height="tableHeight" border stripe>
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="projectNo" label="项目号" min-width="130" align="center"></el-table-column>
<el-table-column prop="floors" label="楼层" min-width="80" align="center"></el-table-column>
<el-table-column prop="planFinishDate" label="计划完工日期" min-width="130" align="center"></el-table-column>
<el-table-column label="仓库配料" width="105" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'stocking')"></span></template></el-table-column>
<el-table-column label="组装" width="90" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'assy')"></span></template></el-table-column>
<el-table-column label="检验" width="90" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'inspect')"></span></template></el-table-column>
<el-table-column label="打包" width="90" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'pack')"></span></template></el-table-column>
<el-table-column prop="status" label="订单状态" width="100" align="center"><template slot-scope="scope"><el-tag class="board-tag" :class="getOrderStatusClass(scope.row.status)" size="small">{{ scope.row.status }}</el-tag></template></el-table-column>
<el-table-column prop="finishDate" label="完工时间" width="120" align="center"><template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template></el-table-column>
</el-table>
<div class="board-section">
<div class="section-card section-top">
<div class="section-header">
<span class="section-title">VL2.5升级订单进度</span>
<span class="section-count"> {{ renovationPage + 1 }}/{{ renovationPageCount }} · 显示 {{ displayRenovationList.length }} / {{ renovationList.length }} </span>
</div>
<el-table class="board-table" :data="displayRenovationList" :height="tableHeightTop" border stripe>
<el-table-column type="index" label="序号" width="54" align="center"></el-table-column>
<el-table-column prop="projectNo" label="项目号" min-width="120" align="center"></el-table-column>
<el-table-column prop="floors" label="楼层" min-width="70" align="center"></el-table-column>
<el-table-column prop="planFinishDate" label="计划完工日期" min-width="120" align="center"></el-table-column>
<el-table-column label="仓库配料" width="90" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'stocking')"></span></template></el-table-column>
<el-table-column label="组装" width="84" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'assy')"></span></template></el-table-column>
<el-table-column label="检验" width="84" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'inspect')"></span></template></el-table-column>
<el-table-column label="打包" width="84" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'pack')"></span></template></el-table-column>
<el-table-column prop="status" label="订单状态" width="96" align="center"><template slot-scope="scope"><el-tag class="board-tag" :class="getStatusTagClass(scope.row.status)" size="small">{{ scope.row.status }}</el-tag></template></el-table-column>
<el-table-column prop="finishDate" label="完工时间" width="118" align="center"><template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template></el-table-column>
</el-table>
</div>
<div class="section-card section-bottom-left">
<div class="section-header">
<span class="section-title">线缆/COP任务进度</span>
<span class="section-count"> {{ cableCopPage + 1 }}/{{ cableCopPageCount }} · 显示 {{ displayCableCopList.length }} / {{ cableCopList.length }} </span>
</div>
<el-table class="board-table" :data="displayCableCopList" :height="tableHeightBottom" border stripe>
<el-table-column type="index" label="序号" width="54" align="center"></el-table-column>
<el-table-column prop="taskNo" label="任务单号" min-width="126" align="center"></el-table-column>
<el-table-column prop="taskType" label="类别" min-width="96" align="center">
<template slot-scope="scope">
<el-tag class="board-tag" :class="getTaskTypeClass(scope.row.taskType)" size="small">{{ scope.row.taskType || '-' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="planFinishDate" label="计划完工日期" min-width="120" align="center"></el-table-column>
<el-table-column prop="taskQty" label="任务数量" min-width="84" align="center"></el-table-column>
<el-table-column prop="reportQty" label="已报工数量" min-width="84" align="center"></el-table-column>
<el-table-column label="线缆生产" width="86" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'lineProduction')"></span></template></el-table-column>
<el-table-column label="COP生产" width="86" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'copProduction')"></span></template></el-table-column>
<el-table-column prop="status" label="任务状态" width="96" align="center">
<template slot-scope="scope">
<el-tag class="board-tag" :class="getStatusTagClass(scope.row.status)" size="small">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="finishDate" label="完工时间" width="118" align="center"><template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template></el-table-column>
</el-table>
</div>
<div class="section-card section-bottom-right">
<div class="section-header">
<span class="section-title">机加工任务进度</span>
<span class="section-count"> {{ machiningPage + 1 }}/{{ machiningPageCount }} · 显示 {{ displayMachiningList.length }} / {{ machiningList.length }} </span>
</div>
<el-table class="board-table" :data="displayMachiningList" :height="tableHeightBottom" border stripe>
<el-table-column type="index" label="序号" width="54" align="center"></el-table-column>
<el-table-column prop="modelNo" label="物料号" min-width="170" align="center"></el-table-column>
<el-table-column prop="taskQty" label="计划数量" min-width="88" align="center"></el-table-column>
<el-table-column prop="reportQty" label="实际数量" min-width="88" align="center"></el-table-column>
<el-table-column prop="planFinishDate" label="计划完工日期" min-width="120" align="center"></el-table-column>
<el-table-column prop="status" label="任务状态" width="96" align="center">
<template slot-scope="scope">
<el-tag class="board-tag" :class="getStatusTagClass(scope.row.status)" size="small">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="finishDate" label="实际完工日期" min-width="120" align="center">
<template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</template>
<script>
import { getRenovationOrderList } from '@/api/longchuang/productionPlan'
const NODE_TEMPLATE = ['stocking', 'assy', 'inspect', 'pack']
import { getCableCopTaskList, getMachiningTaskList, getRenovationOrderList } from '@/api/longchuang/productionPlan'
import { getScreenInnerHeight } from '@/utils/screenAdapt'
const STATUS_ALLOW_LIST = ['已排产', '进行中', '已完成']
const RENOVATION_NODE_TEMPLATE = ['stocking', 'assy', 'inspect', 'pack']
const CABLE_COP_NODE_TEMPLATE = ['lineProduction', 'copProduction']
const BOARD_PAGE_SIZE = 6
export default {
name: 'ScreenRenovationProgress',
data() {
return { loading: false, boardTimerId: null, tableHeight: 640, currentTime: '', timerId: null, boardList: [], kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 } }
return {
loading: false,
boardTimerId: null,
tableHeightTop: 220,
tableHeightBottom: 180,
topRowLimit: BOARD_PAGE_SIZE,
bottomRowLimit: BOARD_PAGE_SIZE,
carouselTimerId: null,
carouselIntervalMs: 8000,
renovationPage: 0,
cableCopPage: 0,
machiningPage: 0,
currentTime: '',
timerId: null,
renovationList: [],
cableCopList: [],
machiningList: [],
renovationKpi: { total: 0, processing: 0, finished: 0, finishRate: 0 },
cableCopKpi: { total: 0, processing: 0, finished: 0, finishRate: 0 },
machiningKpi: { total: 0, processing: 0, finished: 0, finishRate: 0 }
}
},
computed: {
renovationPageCount() {
return this.getPageCount(this.renovationList.length, this.topRowLimit)
},
cableCopPageCount() {
return this.getPageCount(this.cableCopList.length, this.bottomRowLimit)
},
machiningPageCount() {
return this.getPageCount(this.machiningList.length, this.bottomRowLimit)
},
displayRenovationList() {
return this.getPageList(this.renovationList, this.renovationPage, this.topRowLimit)
},
displayCableCopList() {
return this.getPageList(this.cableCopList, this.cableCopPage, this.bottomRowLimit)
},
displayMachiningList() {
return this.getPageList(this.machiningList, this.machiningPage, this.bottomRowLimit)
}
},
mounted() { this.setTableHeight(); this.loadBoardData(); this.updateTime(); this.timerId = setInterval(this.updateTime, 1000);
// 60
mounted() {
this.setTableHeight()
this.loadBoardData()
this.updateTime()
this.timerId = setInterval(this.updateTime, 1000)
this.boardTimerId = setInterval(() => {
this.loadBoardData()
}, 60000)
window.addEventListener('resize', this.setTableHeight) },
beforeDestroy() { if (this.timerId) clearInterval(this.timerId);
if (this.boardTimerId) {
clearInterval(this.boardTimerId)
}
window.removeEventListener('resize', this.setTableHeight) },
this.startCarousel()
window.addEventListener('resize', this.setTableHeight)
},
beforeDestroy() {
if (this.timerId) clearInterval(this.timerId)
if (this.boardTimerId) clearInterval(this.boardTimerId)
this.stopCarousel()
window.removeEventListener('resize', this.setTableHeight)
},
methods: {
setTableHeight() {
this.tableHeight = Math.max(420, window.innerHeight - 240)
const boardGap = 10
const topRatio = 0.56
const reservedHeight = 286
const sectionHeaderHeight = 46
const boardHeight = Math.max(420, getScreenInnerHeight() - reservedHeight)
const topSectionHeight = Math.floor((boardHeight - boardGap) * topRatio)
const bottomSectionHeight = boardHeight - boardGap - topSectionHeight
this.tableHeightTop = Math.max(120, topSectionHeight - sectionHeaderHeight)
this.tableHeightBottom = Math.max(120, bottomSectionHeight - sectionHeaderHeight)
this.topRowLimit = BOARD_PAGE_SIZE
this.bottomRowLimit = BOARD_PAGE_SIZE
this.normalizeCarouselPage()
},
loadBoardData() {
if (this.loading) return
this.loading = true
let statusList = ['已排产', '进行中']
getRenovationOrderList({ page: 1, limit: 300,statusList: statusList }).then(({data}) => {
const params = { page: 1, limit: 300, statusList: STATUS_ALLOW_LIST }
Promise.all([
getRenovationOrderList(params).catch(() => null),
getCableCopTaskList(params).catch(() => null),
getMachiningTaskList(params).catch(() => null)
]).then(([renovationRes, cableCopRes, machiningRes]) => {
this.renovationList = this.buildRenovationList(this.getListFromResponse(renovationRes))
this.cableCopList = this.buildCableCopList(this.getListFromResponse(cableCopRes))
this.machiningList = this.buildMachiningList(this.getListFromResponse(machiningRes))
this.renovationKpi = this.buildKpi(this.renovationList)
this.cableCopKpi = this.buildKpi(this.cableCopList)
this.machiningKpi = this.buildKpi(this.machiningList)
this.normalizeCarouselPage()
}).finally(() => {
this.loading = false
const source = (data && data.code === 0 && data.page && data.page.list) ? data.page.list : this.getMockList()
this.boardList = this.buildBoardList(source)
this.buildKpi()
}).catch(() => { this.loading = false; this.boardList = this.buildBoardList(this.getMockList()); this.buildKpi() })
})
},
getPageCount(total, pageSize) {
if (!pageSize) return 1
return Math.max(1, Math.ceil(total / pageSize))
},
getPageList(list, pageIndex, pageSize) {
const safeSize = Math.max(1, pageSize || 1)
const count = this.getPageCount((list || []).length, safeSize)
const safePage = Math.min(Math.max(pageIndex, 0), count - 1)
const start = safePage * safeSize
return (list || []).slice(start, start + safeSize)
},
normalizeCarouselPage() {
this.renovationPage = Math.min(this.renovationPage, this.renovationPageCount - 1)
this.cableCopPage = Math.min(this.cableCopPage, this.cableCopPageCount - 1)
this.machiningPage = Math.min(this.machiningPage, this.machiningPageCount - 1)
this.renovationPage = Math.max(this.renovationPage, 0)
this.cableCopPage = Math.max(this.cableCopPage, 0)
this.machiningPage = Math.max(this.machiningPage, 0)
},
buildBoardList(sourceList) {
const statusAllow = ['已排产', '进行中', '已完成']
return sourceList.filter(item => statusAllow.includes(item.status)).map(item => ({ ...item, nodeStatusMap: this.toNodeStatusMap(item.nodeList) }))
nextPage(page, pageCount) {
if (pageCount <= 1) return 0
return (page + 1) % pageCount
},
toNodeStatusMap(nodeList) {
startCarousel() {
this.stopCarousel()
this.carouselTimerId = setInterval(() => {
this.renovationPage = this.nextPage(this.renovationPage, this.renovationPageCount)
this.cableCopPage = this.nextPage(this.cableCopPage, this.cableCopPageCount)
this.machiningPage = this.nextPage(this.machiningPage, this.machiningPageCount)
}, this.carouselIntervalMs)
},
stopCarousel() {
if (!this.carouselTimerId) return
clearInterval(this.carouselTimerId)
this.carouselTimerId = null
},
getListFromResponse(response) {
const data = response && response.data
const list = data && data.code === 0 && data.page && Array.isArray(data.page.list) ? data.page.list : null
return list || this.getMockList()
},
buildRenovationList(sourceList) {
return (sourceList || [])
.filter(item => STATUS_ALLOW_LIST.includes(item.status))
.map(item => ({
projectNo: item.projectNo || '',
floors: item.floors || '',
planFinishDate: item.planFinishDate || '',
status: item.status || '',
finishDate: item.finishDate || '',
nodeStatusMap: this.toNodeStatusMap(item.nodeList, RENOVATION_NODE_TEMPLATE)
}))
},
buildCableCopList(sourceList) {
return (sourceList || [])
.filter(item => STATUS_ALLOW_LIST.includes(item.status))
.map(item => ({
taskNo: item.taskNo || '',
sourceProjectNo: item.sourceProjectNo || '',
taskType: item.taskType || '',
planFinishDate: item.planFinishDate || '',
status: item.status || '',
finishDate: item.finishDate || '',
taskQty: item.taskQty == null ? 0 : item.taskQty,
reportQty: item.reportQty == null ? 0 : item.reportQty,
nodeStatusMap: this.toNodeStatusMap(item.nodeList, CABLE_COP_NODE_TEMPLATE)
}))
},
buildMachiningList(sourceList) {
return (sourceList || [])
.filter(item => STATUS_ALLOW_LIST.includes(item.status))
.map(item => ({
modelNo: item.modelNo || '',
planFinishDate: item.planFinishDate || '',
status: item.status || '',
finishDate: item.finishDate || '',
taskQty: item.taskQty == null ? 0 : item.taskQty,
reportQty: item.reportQty == null ? 0 : item.reportQty
}))
},
toNodeStatusMap(nodeList, template) {
const map = {}
NODE_TEMPLATE.forEach(code => { map[code] = '未开始' })
;(template || []).forEach(code => { map[code] = '未开始' })
;(nodeList || []).forEach(node => { map[node.nodeCode] = node.status || '未开始' })
return map
},
buildKpi() {
const total = this.boardList.length
const processing = this.boardList.filter(item => item.status === '进行中').length
const finished = this.boardList.filter(item => item.status === '已完成').length
this.kpi = { total, processing, finished, finishRate: total ? Math.round((finished / total) * 100) : 0 }
buildKpi(list) {
const total = list.length
const processing = list.filter(item => item.status === '进行中').length
const finished = list.filter(item => item.status === '已完成').length
return { total, processing, finished, finishRate: total ? Math.round((finished / total) * 100) : 0 }
},
getNodeCellText(row, code) {
return (row.nodeStatusMap && row.nodeStatusMap[code]) || '未开始'
},
getNodeCellText(row, code) { return row.nodeStatusMap[code] || '未开始' },
getNodeCellClass(row, code) {
const status = this.getNodeCellText(row, code)
if (status === '已完成') return 'node-chip done'
return 'node-chip todo'
},
getOrderStatusClass(status) {
getTaskTypeClass(taskType) {
if (taskType === '线缆自制') return 'type-cable'
if (taskType === 'COP自制') return 'type-cop'
return 'type-default'
},
getStatusTagClass(status) {
if (status === '已完成') return 'status-done'
if (status === '进行中') return 'status-doing'
return 'status-planned'
@ -114,37 +346,52 @@ export default {
<style>
.screen-wrap{height:100vh;padding:16px;background:linear-gradient(165deg,#0a1f36 0%,#0f2b47 50%,#0a2037 100%);display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}
.top-bar{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid rgba(109,167,219,.24);background:rgba(9,29,49,.82)}
.top-bar{position:relative}
.top-bar{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid rgba(109,167,219,.24);background:rgba(9,29,49,.82);position:relative}
.header-left{display:flex;align-items:center;min-width:110px;z-index:2}
.logo-box{width:86px;height:30px;border-radius:6px;border:1px solid rgba(128,198,255,.45);color:#d8edff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;background:rgba(36,82,122,.35)}
.header-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;max-width:70%;width:auto;padding:6px 24px 8px;border-radius:8px;border:1px solid rgba(96,170,232,.34);background:linear-gradient(180deg,rgba(33,73,116,.52),rgba(16,44,77,.42))}
.header-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;max-width:72%;width:auto;padding:6px 24px 8px;border-radius:8px;border:1px solid rgba(96,170,232,.34);background:linear-gradient(180deg,rgba(33,73,116,.52),rgba(16,44,77,.42))}
.header-center::before{content:'';position:absolute;left:50%;transform:translateX(-50%);top:-8px;width:68%;height:1px;background:linear-gradient(90deg,rgba(87,164,230,0),rgba(87,164,230,.9),rgba(87,164,230,0))}
.top-bar .tools{margin-left:auto;z-index:2}
.title{color:#8fe7ff;font-size:30px;font-weight:800;letter-spacing:3px;line-height:1.05;text-shadow:0 0 14px rgba(79,179,255,.36)}.subtitle{margin-top:4px;color:#c7e8ff;font-size:12px;letter-spacing:1px}
.tools{display:flex;align-items:center;gap:12px}.time{color:#89d7ff;font-size:20px;font-weight:600}
.kpi-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:12px 0 8px}
.kpi-card{border-radius:10px;padding:12px 14px;background:rgba(14,43,70,.82);border:1px solid rgba(101,157,209,.22)}
.kpi-label{color:#b7d3ee;font-size:13px}.kpi-value{margin-top:6px;font-size:30px;font-weight:700;color:#edf6ff}
.kpi-value.warning{color:#ffd166}.kpi-value.success{color:#74dfa3}.kpi-value.highlight{color:#79d5ff}
.legend-row{display:flex;gap:16px;margin:0 0 10px 2px}.legend-item{display:inline-flex;align-items:center;color:#c7dff4;font-size:12px}
.dot{width:10px;height:10px;border-radius:50%;margin-right:6px;display:inline-block}.dot.done{background:#69e4a4}.dot.doing{background:#ffd35d}.dot.todo{background:#dbe3ee}
.board-table{border-radius:10px;overflow:hidden;border:1px solid rgba(86,140,190,.35);flex:1;min-height:0}
.board-table .cell {
line-height: 30px;
font-size: 16px;
height: 30px;
}
.title{color:#8fe7ff;font-size:37px;font-weight:800;letter-spacing:2px;line-height:1.1;text-shadow:0 0 14px rgba(79,179,255,.36)}
.tools{display:flex;align-items:center;gap:12px}
.time{color:#89d7ff;font-size:26px;font-weight:600}
.summary-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:12px 0 8px}
.summary-card{border-radius:10px;padding:10px 14px;background:rgba(14,43,70,.82);border:1px solid rgba(101,157,209,.22)}
.summary-title{color:#b7d3ee;font-size:16px}
.summary-main{display:flex;align-items:baseline;gap:8px;margin-top:6px}
.summary-rate{font-size:39px;font-weight:700;color:#79d5ff;line-height:1}
.summary-rate-label{color:#9dc5e8;font-size:15px}
.summary-meta{margin-top:4px;color:#d4e6f9;font-size:15px;line-height:1.3}
.legend-row{display:flex;gap:16px;margin:0 0 10px 2px}
.legend-item{display:inline-flex;align-items:center;color:#c7dff4;font-size:15px}
.dot{width:10px;height:10px;border-radius:50%;margin-right:6px;display:inline-block}
.dot.done{background:#69e4a4}
.dot.todo{background:#dbe3ee}
.board-section{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:minmax(0,1.1fr) minmax(0,1fr);gap:10px;flex:1;min-height:0}
.section-card{display:flex;flex-direction:column;min-height:0;border-radius:10px;border:1px solid rgba(86,140,190,.35);background:rgba(12,39,64,.96)}
.section-top{grid-column:1 / span 2}
.section-bottom-left{grid-column:1}
.section-bottom-right{grid-column:2}
.section-header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid rgba(80,133,181,.45)}
.section-title{color:#d7ebff;font-size:17px;font-weight:600}
.section-count{color:#9ec6e9;font-size:15px}
.board-table{border-radius:0;overflow:hidden;flex:1;min-height:0}
.board-table .cell{line-height:36px;font-size:16px;height:36px}
.screen-wrap .board-table .el-table,.screen-wrap .board-table .el-table__expanded-cell,.screen-wrap .board-table .el-table__body-wrapper,.screen-wrap .board-table .el-table__empty-block,.screen-wrap .board-table .el-table__fixed-body-wrapper{background:rgba(12,39,64,.96)!important;color:#fff!important}
.screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:14px!important;padding:12px 0!important}
.screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:46px!important;vertical-align:middle!important}
.screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:16px!important;padding:12px 0!important}
.screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:48px!important;vertical-align:middle!important}
.screen-wrap .board-table .el-table--striped .el-table__body tr.el-table__row--striped>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr.el-table__row--striped>td{background:rgba(29,66,102,.96)!important}
.screen-wrap .board-table .el-table--enable-row-hover .el-table__body tr:hover>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr:hover>td{background:rgba(39,81,123,.96)!important}
.screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:24px!important;overflow:visible!important}
.screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:31px!important;overflow:visible!important}
.screen-wrap .board-table .el-table__body-wrapper{scrollbar-width:none;-ms-overflow-style:none}
.screen-wrap .board-table .el-table__body-wrapper::-webkit-scrollbar{width:0;height:0}
.screen-wrap .board-table .el-table__empty-text{color:#9ac2e7!important}
.node-chip{display:inline-block!important;width:46px;height:14px;margin:0 auto;border-radius:2px}
.node-chip.done{background:#6de3a0}.node-chip.todo{background:#dce4ee}
.board-tag{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:66px!important;height:24px!important;padding:0 10px!important;border-radius:12px!important;border:1px solid transparent!important;font-size:12px!important;font-weight:600!important;line-height:22px!important;box-sizing:border-box!important}
.node-chip.done{background:#6de3a0}
.node-chip.todo{background:#dce4ee}
.board-tag{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:88px!important;height:29px!important;padding:0 10px!important;border-radius:14px!important;border:1px solid transparent!important;font-size:14px!important;font-weight:600!important;line-height:27px!important;box-sizing:border-box!important}
.board-tag.type-cable{color:#73cfff!important;background:rgba(28,88,136,.32)!important;border-color:rgba(115,207,255,.45)!important}
.board-tag.type-cop{color:#7ef0c0!important;background:rgba(31,108,89,.32)!important;border-color:rgba(126,240,192,.45)!important}
.board-tag.type-default{color:#d0e5f9!important;background:rgba(80,118,149,.28)!important;border-color:rgba(208,229,249,.34)!important}
.board-tag.status-planned{color:#b9c7d8!important;background:rgba(96,118,141,.28)!important;border-color:rgba(185,199,216,.34)!important}
.board-tag.status-doing{color:#ffd774!important;background:rgba(120,92,27,.32)!important;border-color:rgba(255,215,116,.45)!important}
.board-tag.status-done{color:#83f3be!important;background:rgba(31,110,84,.32)!important;border-color:rgba(131,243,190,.45)!important}

41
src/views/modules/longtron/screen-whole-lift-progress.vue

@ -43,6 +43,7 @@
<script>
import { getHomeLiftOrderList } from '@/api/longchuang/productionPlan'
import { getScreenInnerHeight } from '@/utils/screenAdapt'
const NODE_TEMPLATE = ['stocking', 'platformDebug', 'bgCeiling', 'doorAssy', 'pack']
@ -85,7 +86,7 @@ export default {
},
methods: {
setTableHeight() {
this.tableHeight = Math.max(420, window.innerHeight - 240)
this.tableHeight = Math.max(420, getScreenInnerHeight() - 280)
},
loadBoardData() {
if (this.loading) return
@ -104,7 +105,17 @@ export default {
},
buildBoardList(sourceList) {
const statusAllow = ['已排产', '进行中', '已完成']
return sourceList.filter(item => statusAllow.includes(item.status)).map(item => ({ ...item, nodeStatusMap: this.toNodeStatusMap(item.nodeList) }))
return sourceList.filter(item => statusAllow.includes(item.status)).map(item => ({
projectNo: item.projectNo || '',
modelNo: item.modelNo || '',
color: item.color || '',
floorCount: item.floorCount || '',
specialRequirement: item.specialRequirement || '',
planDeliveryDate: item.planDeliveryDate || '',
status: item.status || '',
finishDate: item.finishDate || '',
nodeStatusMap: this.toNodeStatusMap(item.nodeList)
}))
},
toNodeStatusMap(nodeList) {
const map = {}
@ -149,35 +160,35 @@ export default {
.top-bar{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid rgba(109,167,219,.24);background:rgba(9,29,49,.82)}
.top-bar{position:relative}
.header-left{display:flex;align-items:center;min-width:110px;z-index:2}
.logo-box{width:86px;height:30px;border-radius:6px;border:1px solid rgba(128,198,255,.45);color:#d8edff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;background:rgba(36,82,122,.35)}
.logo-box{width:86px;height:30px;border-radius:6px;border:1px solid rgba(128,198,255,.45);color:#d8edff;font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;background:rgba(36,82,122,.35)}
.header-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;max-width:70%;width:auto;padding:6px 24px 8px;border-radius:8px;border:1px solid rgba(96,170,232,.34);background:linear-gradient(180deg,rgba(33,73,116,.52),rgba(16,44,77,.42))}
.header-center::before{content:'';position:absolute;left:50%;transform:translateX(-50%);top:-8px;width:68%;height:1px;background:linear-gradient(90deg,rgba(87,164,230,0),rgba(87,164,230,.9),rgba(87,164,230,0))}
.top-bar .tools{margin-left:auto;z-index:2}
.title{color:#8fe7ff;font-size:30px;font-weight:800;letter-spacing:3px;line-height:1.05;text-shadow:0 0 14px rgba(79,179,255,.36)}
.subtitle{margin-top:4px;color:#c7e8ff;font-size:12px;letter-spacing:1px}
.tools{display:flex;align-items:center;gap:12px}.time{color:#89d7ff;font-size:20px;font-weight:600}
.title{color:#8fe7ff;font-size:32px;font-weight:800;letter-spacing:3px;line-height:1.05;text-shadow:0 0 14px rgba(79,179,255,.36)}
.subtitle{margin-top:4px;color:#c7e8ff;font-size:11px;letter-spacing:1px}
.tools{display:flex;align-items:center;gap:12px}.time{color:#89d7ff;font-size:22px;font-weight:600}
.kpi-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:12px 0 8px}
.kpi-card{border-radius:10px;padding:12px 14px;background:rgba(14,43,70,.82);border:1px solid rgba(101,157,209,.22)}
.kpi-label{color:#b7d3ee;font-size:13px}.kpi-value{margin-top:6px;font-size:30px;font-weight:700;color:#edf6ff}
.kpi-label{color:#b7d3ee;font-size:14px}.kpi-value{margin-top:6px;font-size:32px;font-weight:700;color:#edf6ff}
.kpi-value.warning{color:#ffd166}.kpi-value.success{color:#74dfa3}.kpi-value.highlight{color:#79d5ff}
.legend-row{display:flex;gap:16px;margin:0 0 10px 2px}.legend-item{display:inline-flex;align-items:center;color:#c7dff4;font-size:12px}
.legend-row{display:flex;gap:16px;margin:0 0 10px 2px}.legend-item{display:inline-flex;align-items:center;color:#c7dff4;font-size:13px}
.dot{width:10px;height:10px;border-radius:50%;margin-right:6px;display:inline-block}.dot.done{background:#69e4a4}.dot.doing{background:#ffd35d}.dot.todo{background:#dbe3ee}
.board-table{border-radius:10px;overflow:hidden;border:1px solid rgba(86,140,190,.35);flex:1;min-height:0}
.board-table .cell {
line-height: 30px;
font-size: 16px;
height: 30px;
line-height: 36px;
font-size: 17px;
height: 36px;
}
.screen-wrap .board-table .el-table,.screen-wrap .board-table .el-table__expanded-cell,.screen-wrap .board-table .el-table__body-wrapper,.screen-wrap .board-table .el-table__empty-block,.screen-wrap .board-table .el-table__fixed-body-wrapper{background:rgba(12,39,64,.96)!important;color:#fff!important}
.screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:14px!important;padding:12px 0!important}
.screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:46px!important;vertical-align:middle!important}
.screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:15px!important;padding:14px 0!important}
.screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:55px!important;vertical-align:middle!important}
.screen-wrap .board-table .el-table--striped .el-table__body tr.el-table__row--striped>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr.el-table__row--striped>td{background:rgba(29,66,102,.96)!important}
.screen-wrap .board-table .el-table--enable-row-hover .el-table__body tr:hover>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr:hover>td{background:rgba(39,81,123,.96)!important}
.screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:24px!important;overflow:visible!important}
.screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:29px!important;overflow:visible!important}
.screen-wrap .board-table .el-table__empty-text{color:#9ac2e7!important}
.node-chip{display:inline-block!important;width:46px;height:14px;margin:0 auto;border-radius:2px}
.node-chip.done{background:#6de3a0}.node-chip.todo{background:#dce4ee}
.board-tag{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:66px!important;height:24px!important;padding:0 10px!important;border-radius:12px!important;border:1px solid transparent!important;font-size:12px!important;font-weight:600!important;line-height:22px!important;box-sizing:border-box!important}
.board-tag{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:80px!important;height:29px!important;padding:0 10px!important;border-radius:14px!important;border:1px solid transparent!important;font-size:13px!important;font-weight:600!important;line-height:27px!important;box-sizing:border-box!important}
.board-tag.status-planned{color:#b9c7d8!important;background:rgba(96,118,141,.28)!important;border-color:rgba(185,199,216,.34)!important}
.board-tag.status-doing{color:#ffd774!important;background:rgba(120,92,27,.32)!important;border-color:rgba(255,215,116,.45)!important}
.board-tag.status-done{color:#83f3be!important;background:rgba(31,110,84,.32)!important;border-color:rgba(131,243,190,.45)!important}

Loading…
Cancel
Save