本提交完成了多个核心模块的功能完善与业务对齐: 1. 工位终端:固定工位配置、移除自选工位逻辑、适配配置化工位号 2. MES核心:重构工位组合逻辑、新增工单/流程状态管理、补全报工/追溯逻辑 3. WMS客户端:新增修改密码功能、优化帮助弹窗逻辑 4. 文档与权限:补充完整操作手册、统一菜单名称与权限描述 5. 修复多业务校验:排产数量校验、工单状态校验、工位绑定规则
145 lines
5.2 KiB
Vue
145 lines
5.2 KiB
Vue
<template>
|
|
<el-card>
|
|
<el-form inline>
|
|
<el-form-item label="操作人">
|
|
<el-input v-model="query.operator" placeholder="输入操作人" clearable style="width:180px" @keyup.enter="load" />
|
|
</el-form-item>
|
|
<el-form-item label="日期范围">
|
|
<el-date-picker
|
|
v-model="query.range"
|
|
type="daterange"
|
|
value-format="YYYY-MM-DD"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
style="width:260px"
|
|
@change="load" />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="load">查询</el-button>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="success" @click="exportCsv">导出 CSV</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<!-- 汇总卡 -->
|
|
<el-row :gutter="16" style="margin-bottom:14px">
|
|
<el-col :span="6">
|
|
<el-card shadow="never" class="stat-card">
|
|
<div class="stat-label">总完成数</div>
|
|
<div class="stat-value">{{ summary.doneCount }}</div>
|
|
</el-card>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-card shadow="never" class="stat-card">
|
|
<div class="stat-label">总合格数</div>
|
|
<div class="stat-value ok">{{ summary.okCount }}</div>
|
|
</el-card>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-card shadow="never" class="stat-card">
|
|
<div class="stat-label">总NG数</div>
|
|
<div class="stat-value ng">{{ summary.ngCount }}</div>
|
|
</el-card>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-card shadow="never" class="stat-card">
|
|
<div class="stat-label">总合格率</div>
|
|
<div class="stat-value">{{ summary.rate }}</div>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-table :data="rows" border size="small">
|
|
<el-table-column prop="operator" label="操作人" min-width="110" />
|
|
<el-table-column prop="date" label="日期" width="110" />
|
|
<el-table-column prop="processCode" label="工位" width="80" align="center" />
|
|
<el-table-column prop="processName" label="工位名" min-width="130" show-overflow-tooltip />
|
|
<el-table-column prop="doneCount" label="完成数" width="90" align="right" />
|
|
<el-table-column prop="okCount" label="合格数" width="90" align="right" />
|
|
<el-table-column prop="ngCount" label="NG数" width="90" align="right" />
|
|
<el-table-column label="合格率" width="100" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag :type="rateOf(row) >= 100 ? 'success' : (rateOf(row) >= 0 ? 'warning' : 'info')" effect="plain">
|
|
{{ rateText(row) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-card>
|
|
<PageHelp :data="helpPerformance" />
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, computed, onMounted } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import PageHelp from '../components/PageHelp.vue'
|
|
import { helpPerformance } from '../help'
|
|
import request from '../utils/request'
|
|
|
|
const query = reactive({ operator: '', range: null })
|
|
const rows = ref([])
|
|
|
|
const summary = computed(() => {
|
|
let doneCount = 0, okCount = 0, ngCount = 0
|
|
rows.value.forEach((r) => {
|
|
doneCount += Number(r.doneCount) || 0
|
|
okCount += Number(r.okCount) || 0
|
|
ngCount += Number(r.ngCount) || 0
|
|
})
|
|
return {
|
|
doneCount, okCount, ngCount,
|
|
rate: doneCount ? ((okCount / doneCount) * 100).toFixed(1) + '%' : '-'
|
|
}
|
|
})
|
|
|
|
function rateOf(row) {
|
|
const done = Number(row.doneCount) || 0
|
|
const ok = Number(row.okCount) || 0
|
|
return done ? (ok / done) * 100 : -1
|
|
}
|
|
function rateText(row) {
|
|
const r = rateOf(row)
|
|
return r < 0 ? '-' : r.toFixed(1) + '%'
|
|
}
|
|
|
|
async function load() {
|
|
const [from, to] = query.range || []
|
|
const params = {
|
|
operator: query.operator || '',
|
|
from: from || '',
|
|
to: to || ''
|
|
}
|
|
const data = (await request.get('/performance', { params })) || {}
|
|
rows.value = data.rows || []
|
|
}
|
|
|
|
// 导出 CSV(块6):按当前查询结果生成,含 BOM 以便 Excel 打开中文不乱码
|
|
function exportCsv() {
|
|
if (!rows.value.length) return ElMessage.warning('当前无数据可导出')
|
|
const header = ['操作人', '日期', '工位', '工位名', '完成数', '合格数', 'NG数', '合格率']
|
|
const lines = rows.value.map((r) => {
|
|
const rate = Number(r.doneCount) ? ((Number(r.okCount) / Number(r.doneCount)) * 100).toFixed(1) + '%' : '-'
|
|
return [r.operator, r.date, r.processCode, r.processName, r.doneCount, r.okCount, r.ngCount, rate]
|
|
})
|
|
const esc = (v) => `"${String(v ?? '').replace(/"/g, '""')}"`
|
|
const csv = '\ufeff' + [header, ...lines].map((row) => row.map(esc).join(',')).join('\r\n')
|
|
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
|
|
const a = document.createElement('a')
|
|
a.href = URL.createObjectURL(blob)
|
|
a.download = `绩效报表_${new Date().toISOString().slice(0, 10)}.csv`
|
|
a.click()
|
|
URL.revokeObjectURL(a.href)
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.stat-card { text-align: center; }
|
|
.stat-label { color: #909399; font-size: 13px; margin-bottom: 6px; }
|
|
.stat-value { font-size: 26px; font-weight: 700; color: #303133; }
|
|
.stat-value.ok { color: #67c23a; }
|
|
.stat-value.ng { color: #f56c6c; }
|
|
</style>
|