feat: update test task dashboard
This commit is contained in:
@@ -33,6 +33,50 @@
|
||||
</section>
|
||||
<section class="main-grid">
|
||||
<div class="left-column">
|
||||
<article class="panel table-panel test-summary-panel">
|
||||
<div class="panel-caption">测试任务汇总</div>
|
||||
<div class="summary-wrap">
|
||||
<table class="test-summary-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="summary-group-th">项目</th>
|
||||
<th class="summary-metric-th">指标</th>
|
||||
<th class="summary-count-th">数量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in testSummaryRows" :key="`${row.groupKey}-${row.metricKey}`">
|
||||
<td v-if="row.groupRowspan" :rowspan="row.groupRowspan" class="summary-group-cell">
|
||||
{{ row.group }}
|
||||
</td>
|
||||
<td class="summary-metric-cell" :class="`metric-${row.metricKey}`">{{ row.metric }}</td>
|
||||
<td class="summary-count-cell" :class="`metric-${row.metricKey}`">{{ row.value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
<article class="panel table-panel">
|
||||
<div class="panel-caption">已完工任务</div>
|
||||
<div class="scroll-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="idx-th">序号</th>
|
||||
<th v-for="c in doneColumns" :key="c.key">{{ c.label }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody :style="doneScrollStyle">
|
||||
<tr v-for="(r, i) in doneDisplayRows" :key="i">
|
||||
<td class="idx-td">{{ r._idx }}</td>
|
||||
<td v-for="c in doneColumns" :key="c.key" :class="c.key === 'passRate' ? r.rateClass : ''">{{ r[c.key] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<aside class="right-column">
|
||||
<article class="panel table-panel">
|
||||
<div class="panel-caption">发货状态列表</div>
|
||||
<div class="scroll-wrap">
|
||||
@@ -71,46 +115,6 @@
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<aside class="right-column">
|
||||
<article class="panel table-panel">
|
||||
<div class="panel-caption">在制任务缺件明细</div>
|
||||
<div class="scroll-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="idx-th">序号</th>
|
||||
<th v-for="c in materialColumns" :key="c.key">{{ c.label }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody :style="materialScrollStyle">
|
||||
<tr v-for="(r, i) in materialDisplayRows" :key="i">
|
||||
<td class="idx-td">{{ r._idx }}</td>
|
||||
<td v-for="c in materialColumns" :key="c.key">{{ r[c.key] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
<article class="panel table-panel">
|
||||
<div class="panel-caption">已完工任务</div>
|
||||
<div class="scroll-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="idx-th">序号</th>
|
||||
<th v-for="c in doneColumns" :key="c.key">{{ c.label }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody :style="doneScrollStyle">
|
||||
<tr v-for="(r, i) in doneDisplayRows" :key="i">
|
||||
<td class="idx-td">{{ r._idx }}</td>
|
||||
<td v-for="c in doneColumns" :key="c.key" :class="c.key === 'passRate' ? r.rateClass : ''">{{ r[c.key] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
@@ -163,23 +167,32 @@ onUnmounted(() => {
|
||||
// ============ 滚动数据 ============
|
||||
const deliveryRows = ref([])
|
||||
const taskRows = ref([])
|
||||
const materialRows = ref([])
|
||||
const doneRows = ref([])
|
||||
const rawTestAssemblyRows = ref([])
|
||||
const stagnationRows = ref([])
|
||||
|
||||
// 每个列表的滚动偏移
|
||||
const deliveryOffset = ref(0)
|
||||
const taskOffset = ref(0)
|
||||
const materialOffset = ref(0)
|
||||
const doneOffset = ref(0)
|
||||
|
||||
// 可见行数
|
||||
const VISIBLE_ROWS = {
|
||||
delivery: 8,
|
||||
task: 7,
|
||||
material: 8,
|
||||
done: 7
|
||||
}
|
||||
|
||||
const testSummaryGroups = [
|
||||
{ key: 'valve', label: '阀类' },
|
||||
{ key: 'system', label: '系统类' }
|
||||
]
|
||||
|
||||
const testSummaryMetrics = [
|
||||
{ key: 'urgent', label: '加急' },
|
||||
{ key: 'stagnation', label: '滞留' }
|
||||
]
|
||||
|
||||
// 生成带序号和循环滚动的显示行
|
||||
const makeDisplayRows = (rows, offset, visibleCount) => {
|
||||
if (!rows || rows.length === 0) return []
|
||||
@@ -194,13 +207,29 @@ const makeDisplayRows = (rows, offset, visibleCount) => {
|
||||
|
||||
const deliveryDisplayRows = computed(() => makeDisplayRows(deliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery))
|
||||
const taskDisplayRows = computed(() => makeDisplayRows(taskRows.value, taskOffset.value, VISIBLE_ROWS.task))
|
||||
const materialDisplayRows = computed(() => makeDisplayRows(materialRows.value, materialOffset.value, VISIBLE_ROWS.material))
|
||||
const doneDisplayRows = computed(() => makeDisplayRows(doneRows.value, doneOffset.value, VISIBLE_ROWS.done))
|
||||
const testSummaryRows = computed(() => {
|
||||
const rows = []
|
||||
|
||||
testSummaryGroups.forEach(group => {
|
||||
testSummaryMetrics.forEach(metric => {
|
||||
rows.push({
|
||||
group: group.label,
|
||||
groupKey: group.key,
|
||||
groupRowspan: metric.key === testSummaryMetrics[0].key ? testSummaryMetrics.length : 0,
|
||||
metric: metric.label,
|
||||
metricKey: metric.key,
|
||||
value: countTestSummaryRows(group.key, metric.key)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return rows
|
||||
})
|
||||
|
||||
// 滚动样式(平滑过渡)
|
||||
const deliveryScrollStyle = computed(() => ({ transition: 'none' }))
|
||||
const taskScrollStyle = computed(() => ({ transition: 'none' }))
|
||||
const materialScrollStyle = computed(() => ({ transition: 'none' }))
|
||||
const doneScrollStyle = computed(() => ({ transition: 'none' }))
|
||||
|
||||
// 启动单个列表滚动
|
||||
@@ -217,7 +246,6 @@ const startScroll = (rowsRef, offsetRef) => {
|
||||
const startAllScroll = () => {
|
||||
startScroll(deliveryRows, deliveryOffset)
|
||||
startScroll(taskRows, taskOffset)
|
||||
startScroll(materialRows, materialOffset)
|
||||
startScroll(doneRows, doneOffset)
|
||||
}
|
||||
|
||||
@@ -230,8 +258,8 @@ const stopAllScroll = () => {
|
||||
const searchDashboardData = () => {
|
||||
searchTestAssemblyTaskData()
|
||||
searchQualityTimelinessData()
|
||||
searchStagnationData()
|
||||
searchDoneTaskData()
|
||||
searchMaterialshortageData()
|
||||
searchDeliveryData()
|
||||
}
|
||||
const searchTestAssemblyTaskData = () => {
|
||||
@@ -264,29 +292,44 @@ const searchDoneTaskData = () => {
|
||||
]
|
||||
queryDashboardData('质量管理_测试装配任务_完成查询', applyDoneTaskData, param)
|
||||
}
|
||||
const searchStagnationData = () => {
|
||||
const param = [
|
||||
['合同号', ''],
|
||||
['订单号', ''],
|
||||
['零件编号', ''],
|
||||
['零件名称', ''],
|
||||
['完工日期_Start', ''],
|
||||
['完工日期_End', ''],
|
||||
['检测日期_Start', ''],
|
||||
['检测日期_End', ''],
|
||||
['检测结果', ''],
|
||||
['检测人', ''],
|
||||
['检测类别', ''],
|
||||
['属性', '2']
|
||||
]
|
||||
queryDashboardData('质量管理_及时检验报表_查询', applyStagnationData, param)
|
||||
}
|
||||
const searchDeliveryData = () => {
|
||||
queryDashboardData('装配中心任务看板_质检发货状态查询', applyDeliveryData)
|
||||
}
|
||||
const searchMaterialshortageData = () => {
|
||||
queryDashboardData('装配中心任务看板_在制品缺件查询', applyMaterialshortageData)
|
||||
}
|
||||
const applyTestAssemblyTaskData = rows => {
|
||||
const sortedRows = sortTestAssemblyTaskRows(rows)
|
||||
rawTestAssemblyRows.value = sortedRows
|
||||
const executableRows = sortedRows.filter(isInspectionUnfinished)
|
||||
const valueRows = executableRows.filter(item => getTaskType(item) === '阀类')
|
||||
const systemRows = executableRows.filter(item => getTaskType(item) === '系统类')
|
||||
|
||||
summaryCards[0].metrics[0].value = valueRows.length
|
||||
summaryCards[0].metrics[1].value = systemRows.length
|
||||
summaryCards[1].value = executableRows.filter(isReworkTask).length
|
||||
summaryCards[3].value = executableRows.filter(item => getNumber(item.加急总数 || item.加急数量) > 0).length
|
||||
summaryCards[0].value = executableRows.filter(isReworkTask).length
|
||||
summaryCards[1].value = sortedRows.filter(isUrgentTask).length
|
||||
summaryCards[4].value = sortedRows.reduce((sum, item) => sum + getNumber(item.异常未关闭数 || item.测试装配异常未关闭总数), 0)
|
||||
|
||||
applyTaskData(sortedRows)
|
||||
}
|
||||
const applyQualityTimelinessData = rows => {
|
||||
const row = getFirstRow(rows)
|
||||
summaryCards[2].value = `${getNumber(row.质检及时率).toFixed(2)}`
|
||||
summaryCards[3].value = `${getNumber(row.质检及时率).toFixed(2)}`
|
||||
}
|
||||
const applyStagnationData = rows => {
|
||||
stagnationRows.value = rows
|
||||
summaryCards[2].value = rows.length
|
||||
}
|
||||
const applyDeliveryData = rows => {
|
||||
deliveryRows.value = rows.map(item => {
|
||||
@@ -344,16 +387,6 @@ const applyTaskData = rows => {
|
||||
expectedInspectionDate: formatDate(item.预计送检日期)
|
||||
}))
|
||||
}
|
||||
const applyMaterialshortageData = rows => {
|
||||
console.log(rows)
|
||||
materialRows.value = rows.map(item => ({
|
||||
wipOrder: item.DocEntry,
|
||||
shortageStatus: item.U_Name,
|
||||
finishTime: item.ItemCode,
|
||||
owner: item.加急数量
|
||||
}))
|
||||
console.log(materialRows.value)
|
||||
}
|
||||
const queryDashboardData = (procedureName, applyData, param = []) => {
|
||||
let data
|
||||
data = CreateData('11', procedureName, param)
|
||||
@@ -396,6 +429,22 @@ const isInspectionUnfinished = item => getNumber(item.完成数量) > getNumber(
|
||||
|
||||
const isReworkTask = item => String(item.检测说明 || '').includes('返工')
|
||||
|
||||
const isUrgentTask = item => getNumber(item.加急总数 || item.加急数量) > 0
|
||||
|
||||
const getTaskGroupKey = item => {
|
||||
const taskType = String(getTaskType(item))
|
||||
const text = String(item.自制件属性 || item.物料名称 || item.零件名称 || item.物料描述 || '')
|
||||
return taskType.includes('系统') || text.includes('系统') ? 'system' : 'valve'
|
||||
}
|
||||
|
||||
const countTestSummaryRows = (groupKey, metricKey) => {
|
||||
const sourceRows = metricKey === 'stagnation' ? stagnationRows.value : rawTestAssemblyRows.value
|
||||
return sourceRows
|
||||
.filter(item => getTaskGroupKey(item) === groupKey)
|
||||
.filter(item => metricKey === 'urgent' ? isUrgentTask(item) : true)
|
||||
.length
|
||||
}
|
||||
|
||||
const sortTestAssemblyTaskRows = rows => rows.slice().sort((a, b) => {
|
||||
const unfinishedA = isInspectionUnfinished(a)
|
||||
const unfinishedB = isInspectionUnfinished(b)
|
||||
@@ -441,13 +490,13 @@ const icons = {
|
||||
shield: svgIcon('#3a9aff', '#0861e8', '<path d="M36 14l16 7v13c0 11-7 18-16 24-9-6-16-13-16-24V21z"/><path d="M28 37l6 6 12-14"/>'),
|
||||
done: svgIcon('#47eaa0', '#0da76b', '<rect x="19" y="19" width="34" height="34" rx="8"/><path d="M27 37l7 7 15-18"/>')
|
||||
}
|
||||
const summaryCards = [
|
||||
{ title: '可执行任务数', unit: '条', className: 'executable-card', metrics: [{ label: '阀类', value: 0 }, { label: '系统类', value: 0 }] },
|
||||
{ title: '待协助任务数', value: 0, unit: '条', icon: icons.pending , valueClass: 'red-text'},
|
||||
const summaryCards = reactive([
|
||||
{ title: '待协助任务数', value: 0, unit: '条', icon: icons.pending, valueClass: 'red-text' },
|
||||
{ title: '加急汇总', value: 0, unit: '条', icon: icons.urgent, valueClass: 'red-text' },
|
||||
{ title: '滞留汇总', value: 0, unit: '条', icon: icons.returnDown, valueClass: 'orange-text' },
|
||||
{ title: '质检及时率', value: '100.00', icon: icons.finalCheck, valueClass: 'green-text full-value' },
|
||||
{ title: '加急任务数', value: 125, unit: '条', icon: icons.urgent, valueClass: 'red-text' },
|
||||
{ title: '异常未关闭消息数', value: 5, unit: '条', icon: icons.urgent, valueClass: 'red-text', className: 'exception-card' },
|
||||
]
|
||||
{ title: '异常未关闭消息数', value: 0, unit: '条', icon: icons.urgent, valueClass: 'red-text', className: 'exception-card' }
|
||||
])
|
||||
const taskColumns = [
|
||||
{ label: '生产订单', key: 'productionOrder', width: '72px' },
|
||||
{ label: '销售订单', key: 'salesOrder', width: '92px' },
|
||||
@@ -473,12 +522,6 @@ const taskColumns2 = [
|
||||
{ label: '操作人', key: 'urgentQty', width: '62px' },
|
||||
{ label: '装配开工', key: 'operator', width: '82px' },
|
||||
]
|
||||
const materialColumns = [
|
||||
{ label: '生产订单', key: 'wipOrder', width: '76px' },
|
||||
{ label: '物料名称', key: 'shortageStatus', width: '105px' },
|
||||
{ label: '物料编码', key: 'finishTime', width: '160px' },
|
||||
{ label: '加急数量', key: 'owner', width: '90px' },
|
||||
]
|
||||
const doneColumns = [
|
||||
{ label: '生产订单', key: 'productionOrder', width: '88px' },
|
||||
{ label: '物料名称', key: 'materialName', width: '150px' },
|
||||
@@ -572,22 +615,23 @@ const doneColumns = [
|
||||
|
||||
.main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2.05fr 1fr;
|
||||
grid-template-columns: 610px 1fr;
|
||||
gap: 17px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
display: grid;
|
||||
grid-template-rows: 1.05fr .9fr;
|
||||
grid-template-rows: 1fr .88fr;
|
||||
gap: 17px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
display: grid;
|
||||
grid-template-rows: 1.05fr .9fr;
|
||||
grid-template-rows: .92fr 1.08fr;
|
||||
gap: 17px;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -837,6 +881,7 @@ const doneColumns = [
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.summary-wrap,
|
||||
.scroll-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
@@ -884,6 +929,51 @@ th {
|
||||
height: 47px;
|
||||
}
|
||||
|
||||
.test-summary-table th {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.test-summary-table td {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
.summary-group-th {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.summary-metric-th {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.summary-count-th {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.summary-group-cell {
|
||||
color: #ffffff;
|
||||
font-size: 30px;
|
||||
font-weight: 800;
|
||||
background: rgba(7, 74, 112, 0.58);
|
||||
}
|
||||
|
||||
.summary-metric-cell {
|
||||
font-size: 28px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-count-cell {
|
||||
font-size: 44px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.metric-urgent {
|
||||
color: #ff4451;
|
||||
}
|
||||
|
||||
.metric-stagnation {
|
||||
color: #ffb13b;
|
||||
}
|
||||
|
||||
.warning td {
|
||||
color: #ff4949;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user