first commit
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { CircleCloseFilled, WarningFilled, InfoFilled } from '@element-plus/icons-vue';
|
||||
|
||||
// ==================== 列表 ====================
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const deptOptions = ref([]);
|
||||
const query = reactive({ pageIndex: 1, pageSize: 20, date: '', deptId: null });
|
||||
|
||||
async function loadDepts() {
|
||||
try {
|
||||
const res = await request.get('/sys/department');
|
||||
deptOptions.value = (res.data || []).filter((d) => d.name.includes('前缫'));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const params = { pageIndex: query.pageIndex, pageSize: query.pageSize };
|
||||
if (query.date) params.date = query.date;
|
||||
if (query.deptId) params.deptId = query.deptId;
|
||||
const res = await request.get('/xdjy', { params });
|
||||
list.value = res.data.items;
|
||||
total.value = res.data.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageChange(p) { query.pageIndex = p; load(); }
|
||||
|
||||
// ==================== 编辑对话框 ====================
|
||||
const dialogVisible = ref(false);
|
||||
const editLoading = ref(false);
|
||||
const saving = ref(false);
|
||||
const editData = ref(null);
|
||||
const gg = computed(() => editData.value?.guigeConfig || null);
|
||||
|
||||
// 纤度列配置
|
||||
const xdColumns = [
|
||||
{ key: 'xd1', label: '纤度1' },
|
||||
{ key: 'xd2', label: '纤度2' },
|
||||
{ key: 'xd3', label: '纤度3' },
|
||||
{ key: 'xd4', label: '纤度4' },
|
||||
];
|
||||
|
||||
function num(v) {
|
||||
if (typeof v === 'number') return isFinite(v) ? v : 0;
|
||||
if (v === null || v === undefined || v === '') return 0;
|
||||
const n = Number(v);
|
||||
return isFinite(n) ? n : 0;
|
||||
}
|
||||
function round2(v) { return Math.round(v * 100) / 100; }
|
||||
|
||||
// 野纤等级:teye(特野) / daye(大野) / xiaoye(小野) / null(正常)
|
||||
function xdLevel(xd) {
|
||||
const g = gg.value;
|
||||
const v = num(xd);
|
||||
if (!g || v <= 0) return null;
|
||||
if (v <= g.teyeLolimit || v >= g.teyeUplimit) return 'teye';
|
||||
if (v <= g.dayeLolimit || v >= g.dayeUplimit) return 'daye';
|
||||
if (v <= g.xiaoyeLolimit || v >= g.xiaoyeUplimit) return 'xiaoye';
|
||||
return null;
|
||||
}
|
||||
|
||||
// 范围(在中心范围内)
|
||||
function isInRange(xd) {
|
||||
const g = gg.value;
|
||||
const v = num(xd);
|
||||
return !!g && v > 0 && g.centerrangeLolimit <= v && v <= g.centerrangeUplimit;
|
||||
}
|
||||
|
||||
// 中心(等于中心纤度)
|
||||
function isCenter(xd) {
|
||||
const g = gg.value;
|
||||
const v = num(xd);
|
||||
return !!g && v > 0 && g.centerXiandu === v;
|
||||
}
|
||||
|
||||
// 计算单行指标
|
||||
function calcRow(row) {
|
||||
const xds = [num(row.xd1), num(row.xd2), num(row.xd3), num(row.xd4)];
|
||||
const valid = xds.filter((v) => v > 0);
|
||||
row.xdNum = valid.length;
|
||||
if (valid.length > 0) {
|
||||
row.xdAvg = round2(xds.reduce((a, b) => a + b, 0) / valid.length);
|
||||
row.fanweiNum = xds.filter(isInRange).length;
|
||||
row.xiaoyeNum = xds.filter((v) => xdLevel(v) === 'xiaoye').length;
|
||||
row.dayeNum = xds.filter((v) => xdLevel(v) === 'daye').length;
|
||||
row.teyeNum = xds.filter((v) => xdLevel(v) === 'teye').length;
|
||||
row.centerNum = xds.filter(isCenter).length;
|
||||
} else {
|
||||
row.xdAvg = 0; row.fanweiNum = 0; row.xiaoyeNum = 0; row.dayeNum = 0; row.teyeNum = 0; row.centerNum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 汇总指标
|
||||
const summary = computed(() => {
|
||||
const items = editData.value?.xdjy || [];
|
||||
let jiaoshu = 0, sum = 0, fanweiNum = 0, xiaoyeNum = 0, dayeNum = 0, teyeNum = 0, centerNum = 0;
|
||||
const xds = [];
|
||||
items.forEach((r) => {
|
||||
[num(r.xd1), num(r.xd2), num(r.xd3), num(r.xd4)].forEach((xd) => {
|
||||
if (xd > 0) { jiaoshu++; sum += xd; xds.push(xd); }
|
||||
});
|
||||
fanweiNum += r.fanweiNum || 0; xiaoyeNum += r.xiaoyeNum || 0; dayeNum += r.dayeNum || 0;
|
||||
teyeNum += r.teyeNum || 0; centerNum += r.centerNum || 0;
|
||||
});
|
||||
const avg = jiaoshu > 0 ? sum / jiaoshu : 0;
|
||||
let junfangcha = 0;
|
||||
if (jiaoshu > 0) {
|
||||
let diff = 0;
|
||||
xds.forEach((x) => { const d = x - avg; diff += d * d; });
|
||||
junfangcha = round2(Math.sqrt(round2(diff / jiaoshu)));
|
||||
}
|
||||
let zongcha = 0;
|
||||
if (jiaoshu > 0) {
|
||||
xds.sort((a, b) => a - b);
|
||||
const n = Math.max(1, Math.ceil(xds.length * 0.02));
|
||||
const minAvg = xds.slice(0, n).reduce((a, b) => a + b, 0) / n;
|
||||
const maxAvg = xds.slice(xds.length - n).reduce((a, b) => a + b, 0) / n;
|
||||
zongcha = round2(Math.max(Math.abs(minAvg - round2(avg)), Math.abs(maxAvg - round2(avg))));
|
||||
}
|
||||
const fanxianLv = jiaoshu > 0 ? Math.round(fanweiNum * 100 / jiaoshu) : 0;
|
||||
return { jiaoshu, avgxiandu: round2(avg), junfangcha, zongcha, fanxianLv, fanweiNum, xiaoyeNum, dayeNum, teyeNum, centerNum };
|
||||
});
|
||||
|
||||
// 纤度输入:显式赋值 + 重算,避免事件时序问题
|
||||
function onXdInput(row, key, v) {
|
||||
row[key] = num(v);
|
||||
calcRow(row);
|
||||
}
|
||||
|
||||
// 单击选中整个单元格(聚焦时全选,方便直接覆盖输入)
|
||||
function selectAll(e) {
|
||||
const el = e?.target;
|
||||
if (el && typeof el.select === 'function') {
|
||||
setTimeout(() => el.select(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
async function openEdit(row) {
|
||||
editLoading.value = true;
|
||||
dialogVisible.value = true;
|
||||
try {
|
||||
const res = await request.get(`/xdjy/${row.lsjlId}`);
|
||||
editData.value = res.data;
|
||||
editData.value.xdjy.forEach(calcRow);
|
||||
} catch (e) {
|
||||
dialogVisible.value = false;
|
||||
} finally {
|
||||
editLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!editData.value) return;
|
||||
if (summary.value.jiaoshu === 0) { ElMessage.warning('请至少录入一个纤度值'); return; }
|
||||
saving.value = true;
|
||||
try {
|
||||
await request.post('/xdjy', {
|
||||
lsjlId: editData.value.lsjlId,
|
||||
dengji: editData.value.dengji || '',
|
||||
items: editData.value.xdjy.map((i) => ({
|
||||
chetaihao: i.chetaihao,
|
||||
xd1: num(i.xd1), xd2: num(i.xd2), xd3: num(i.xd3), xd4: num(i.xd4)
|
||||
}))
|
||||
});
|
||||
ElMessage.success('保存成功');
|
||||
dialogVisible.value = false;
|
||||
load();
|
||||
} catch (e) {} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => { load(); loadDepts(); });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>纤度检验</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="search-bar">
|
||||
<el-date-picker v-model="query.date" type="date" placeholder="日期" value-format="YYYY-MM-DD" style="width: 160px" />
|
||||
<el-select v-model="query.deptId" clearable placeholder="车间" style="width: 180px">
|
||||
<el-option v-for="d in deptOptions" :key="d.id" :label="d.name" :value="d.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="query.pageIndex=1;load()">搜索</el-button>
|
||||
<el-button @click="Object.assign(query,{date:'',deptId:null,pageIndex:1});load()">重置</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column type="index" label="序号" width="55" />
|
||||
<el-table-column label="日期" width="100">
|
||||
<template #default="{ row }">{{ row.workDate?.slice(0, 10) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deptName" label="车间" width="110" />
|
||||
<el-table-column prop="jizu" label="机组" width="60" />
|
||||
<el-table-column prop="huishu" label="回数" width="60" />
|
||||
<el-table-column prop="guige" label="规格" width="75" />
|
||||
<el-table-column prop="zhuangKouName" label="庄口" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="yanse" label="颜色" width="75" />
|
||||
<el-table-column prop="isbancheName" label="半车" width="65" />
|
||||
<el-table-column prop="chetaishu" label="车台数" width="70" />
|
||||
<el-table-column prop="luosiTime" label="落丝时间" width="90" />
|
||||
<el-table-column prop="yingchengnum" label="应秤数" width="70" />
|
||||
<el-table-column label="已秤数" width="70" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.yingchengnum > row.jiaoshu ? 'warning' : 'success'" size="small" effect="light">{{ row.jiaoshu }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="avgxiandu" label="平均纤度" width="85" />
|
||||
<el-table-column prop="junfangcha" label="均方差" width="75" />
|
||||
<el-table-column prop="zongcha" label="总差" width="65" />
|
||||
<el-table-column prop="fanxianLv" label="范纤率" width="70" />
|
||||
<el-table-column prop="centerNum" label="中心" width="60" />
|
||||
<el-table-column prop="xiaoyeNum" label="小野" width="60" />
|
||||
<el-table-column prop="dayeNum" label="大野" width="60" />
|
||||
<el-table-column prop="teyeNum" label="特野" width="60" />
|
||||
<el-table-column prop="dengji" label="等级" width="65" />
|
||||
<el-table-column label="操作" width="90" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" :type="row.isChecked ? 'warning' : 'primary'" @click="openEdit(row)">
|
||||
{{ row.isChecked ? '重新检验' : '检验' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination class="pagination" :current-page="query.pageIndex" :page-size="query.pageSize" :total="total" layout="total, prev, pager, next" @current-change="handlePageChange" />
|
||||
|
||||
<!-- 检验对话框 -->
|
||||
<el-dialog v-model="dialogVisible" title="纤度检验" width="94%" top="2vh" destroy-on-close>
|
||||
<div v-loading="editLoading">
|
||||
<template v-if="editData">
|
||||
<!-- 落丝信息 -->
|
||||
<el-descriptions :column="6" border size="small" class="info-block">
|
||||
<el-descriptions-item label="车间">{{ editData.deptName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="机组">{{ editData.jizu }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回数">{{ editData.huishu }}</el-descriptions-item>
|
||||
<el-descriptions-item label="庄口">{{ editData.zhuangKouName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="颜色">{{ editData.yanse }}</el-descriptions-item>
|
||||
<el-descriptions-item label="半车">{{ editData.isbancheName }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 规格范围 -->
|
||||
<el-descriptions v-if="gg" :column="6" border size="small" class="info-block">
|
||||
<el-descriptions-item label="规格">{{ gg.guige }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中心范围">[{{ gg.centerrangeLolimit }} - {{ gg.centerrangeUplimit }}]</el-descriptions-item>
|
||||
<el-descriptions-item label="中心纤度">{{ gg.centerXiandu }}</el-descriptions-item>
|
||||
<el-descriptions-item label="小野范围" class="d-xiaoye">≤{{ gg.xiaoyeLolimit }} / ≥{{ gg.xiaoyeUplimit }}</el-descriptions-item>
|
||||
<el-descriptions-item label="大野范围" class="d-daye">≤{{ gg.dayeLolimit }} / ≥{{ gg.dayeUplimit }}</el-descriptions-item>
|
||||
<el-descriptions-item label="特野范围" class="d-teye">≤{{ gg.teyeLolimit }} / ≥{{ gg.teyeUplimit }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 车台明细 -->
|
||||
<el-table :data="editData.xdjy" border size="small" max-height="440">
|
||||
<el-table-column prop="chetaihao" label="车台号" width="70" align="center" fixed />
|
||||
<el-table-column v-for="c in xdColumns" :key="c.key" :label="c.label" width="118" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row[c.key]"
|
||||
:min="0"
|
||||
:precision="1"
|
||||
:step="0.5"
|
||||
:controls="false"
|
||||
:class="['xd-input', 'xd-' + (xdLevel(row[c.key]) || 'normal')]"
|
||||
@focus="selectAll"
|
||||
@input="(v) => onXdInput(row, c.key, v)"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon v-if="xdLevel(row[c.key]) === 'teye'" class="xd-icon icon-teye"><CircleCloseFilled /></el-icon>
|
||||
<el-icon v-else-if="xdLevel(row[c.key]) === 'daye'" class="xd-icon icon-daye"><WarningFilled /></el-icon>
|
||||
<el-icon v-else-if="xdLevel(row[c.key]) === 'xiaoye'" class="xd-icon icon-xiaoye"><InfoFilled /></el-icon>
|
||||
</template>
|
||||
</el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="xdNum" label="已称绞数" width="80" align="center" />
|
||||
<el-table-column prop="xdAvg" label="平均纤度" width="85" align="center" />
|
||||
<el-table-column prop="fanweiNum" label="范围" width="60" align="center" />
|
||||
<el-table-column prop="xiaoyeNum" label="小野" width="60" align="center" />
|
||||
<el-table-column prop="dayeNum" label="大野" width="60" align="center" />
|
||||
<el-table-column prop="teyeNum" label="特野" width="60" align="center" />
|
||||
<el-table-column prop="centerNum" label="中心" width="60" align="center" />
|
||||
</el-table>
|
||||
|
||||
<!-- 汇总 -->
|
||||
<div class="summary-bar">
|
||||
<div class="stat"><span class="lbl">已称绞数</span><b>{{ summary.jiaoshu }}</b></div>
|
||||
<div class="stat"><span class="lbl">平均纤度</span><b class="c-primary">{{ summary.avgxiandu }}</b></div>
|
||||
<div class="stat"><span class="lbl">均方差</span><b>{{ summary.junfangcha }}</b></div>
|
||||
<div class="stat"><span class="lbl">总差</span><b>{{ summary.zongcha }}</b></div>
|
||||
<div class="stat"><span class="lbl">范纤率</span><b class="c-primary">{{ summary.fanxianLv }}%</b></div>
|
||||
<div class="stat"><span class="lbl">范围</span><b class="c-blue">{{ summary.fanweiNum }}</b></div>
|
||||
<div class="stat tag-xiaoye"><span class="lbl">小野</span><b>{{ summary.xiaoyeNum }}</b></div>
|
||||
<div class="stat tag-daye"><span class="lbl">大野</span><b>{{ summary.dayeNum }}</b></div>
|
||||
<div class="stat tag-teye"><span class="lbl">特野</span><b>{{ summary.teyeNum }}</b></div>
|
||||
<div class="stat tag-center"><span class="lbl">中心</span><b>{{ summary.centerNum }}</b></div>
|
||||
<div class="stat level">
|
||||
<span class="lbl">等级</span>
|
||||
<el-input v-model="editData.dengji" placeholder="等级" style="width: 90px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图例 -->
|
||||
<div class="legend">
|
||||
<span class="lg lg-teye"><el-icon><CircleCloseFilled /></el-icon> 特野</span>
|
||||
<span class="lg lg-daye"><el-icon><WarningFilled /></el-icon> 大野</span>
|
||||
<span class="lg lg-xiaoye"><el-icon><InfoFilled /></el-icon> 小野</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header { display: flex; justify-content: space-between; align-items: center; }
|
||||
.search-bar { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.pagination { margin-top: 16px; justify-content: flex-end; }
|
||||
.info-block { margin-bottom: 12px; }
|
||||
|
||||
/* 规格范围描述着色 */
|
||||
.d-xiaoye :deep(.el-descriptions__content) { color: #b88230; font-weight: 600; }
|
||||
.d-daye :deep(.el-descriptions__content) { color: #e6a23c; font-weight: 600; }
|
||||
.d-teye :deep(.el-descriptions__content) { color: #f56c6c; font-weight: 600; }
|
||||
|
||||
/* 纤度输入框:浅色背景 + 图标 */
|
||||
.xd-input :deep(.el-input__wrapper) { padding: 0 6px; }
|
||||
.xd-input.xd-teye :deep(.el-input__wrapper) { background: #fef0f0; box-shadow: 0 0 0 1px #f56c6c inset; }
|
||||
.xd-input.xd-daye :deep(.el-input__wrapper) { background: #fdf6ec; box-shadow: 0 0 0 1px #e6a23c inset; }
|
||||
.xd-input.xd-xiaoye :deep(.el-input__wrapper) { background: #fef9e7; box-shadow: 0 0 0 1px #e6b23c inset; }
|
||||
|
||||
.xd-icon { font-size: 14px; }
|
||||
.icon-teye { color: #f56c6c; }
|
||||
.icon-daye { color: #e6a23c; }
|
||||
.icon-xiaoye { color: #e6b23c; }
|
||||
|
||||
/* 汇总栏 */
|
||||
.summary-bar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 16px; padding: 14px 16px; background: #f5f7fa; border-radius: 8px; }
|
||||
.stat { display: flex; flex-direction: column; align-items: center; min-width: 64px; padding: 6px 12px; background: #fff; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
|
||||
.stat .lbl { font-size: 12px; color: #909399; }
|
||||
.stat b { font-size: 18px; color: #303133; }
|
||||
.stat .c-primary { color: #409eff; }
|
||||
.stat .c-blue { color: #409eff; }
|
||||
.stat.tag-xiaoye b { color: #b88230; }
|
||||
.stat.tag-daye b { color: #e6a23c; }
|
||||
.stat.tag-teye b { color: #f56c6c; }
|
||||
.stat.tag-center b { color: #67c23a; }
|
||||
.stat.level { flex-direction: row; gap: 6px; align-items: center; }
|
||||
|
||||
/* 图例 */
|
||||
.legend { display: flex; gap: 18px; margin-top: 12px; color: #606266; font-size: 13px; }
|
||||
.legend .lg { display: flex; align-items: center; gap: 4px; }
|
||||
.lg-teye { color: #f56c6c; }
|
||||
.lg-daye { color: #e6a23c; }
|
||||
.lg-xiaoye { color: #b88230; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user