|
|
@@ -0,0 +1,1233 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
+ <el-form-item label="测量时间" prop="dataTime">
|
|
|
+ <el-date-picker clearable
|
|
|
+ v-model="queryParams.dataTime"
|
|
|
+ type="date"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ placeholder="请选择测量时间">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="目标值" prop="targetValue">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.targetValue"
|
|
|
+ placeholder="请输入目标值"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
+ <el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ icon="Plus"
|
|
|
+ @click="handleAdd"
|
|
|
+ >新增
|
|
|
+ </el-button>
|
|
|
+ </el-col>
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table v-loading="loading" :data="capList" @selection-change="handleSelectionChange">
|
|
|
+ <el-table-column type="selection" width="55" align="center"/>
|
|
|
+ <el-table-column label="测量时间" align="center" prop="dataTime" width="180">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ parseTime(scope.row.dataTime, '{y}-{m}-{d}') }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="目标值" align="center" prop="targetValue"/>
|
|
|
+ <el-table-column label="误差范围" align="center" prop="toleranceRange">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>±{{ scope.row.toleranceRange !== null ? (scope.row.toleranceRange + '%') : '' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="误差值" align="center" prop="toleranceValue"/>
|
|
|
+ <!-- <el-table-column label="测量值" align="center" prop="measuredValue" />-->
|
|
|
+ <el-table-column label="上限过程能力指数CPU" align="center" prop="cpuValue"/>
|
|
|
+ <el-table-column label="下限过程能力指数CPL" align="center" prop="cplValue"/>
|
|
|
+ <el-table-column label="实际过程能力指数CPK" align="center" prop="cpkValue"/>
|
|
|
+ <el-table-column label="标准差" align="center" prop="stdDeviation"/>
|
|
|
+ <el-table-column label="平均值" align="center" prop="meanValue"/>
|
|
|
+ <el-table-column label="最大值" align="center" prop="maxValue"/>
|
|
|
+ <el-table-column label="最小值" align="center" prop="minValue"/>
|
|
|
+ <el-table-column label="规格上限" align="center" prop="usl"/>
|
|
|
+ <el-table-column label="规格下限" align="center" prop="lsl"/>
|
|
|
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
|
|
|
+ <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
|
|
+ <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <pagination
|
|
|
+ v-show="total>0"
|
|
|
+ :total="total"
|
|
|
+ v-model:page="queryParams.pageNum"
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
+ @pagination="getList"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 添加或修改白坯布重量分析对话框 -->
|
|
|
+ <el-dialog :title="title" v-model="open" style="width: 1366px;height: 768px;" append-to-body>
|
|
|
+ <el-form ref="capRef" :model="form" :rules="rules" label-width="125px">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="测量时间" prop="dataTime">
|
|
|
+ <el-date-picker clearable
|
|
|
+ v-model="form.dataTime"
|
|
|
+ type="date"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ placeholder="请选择测量时间">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="目标值" prop="targetValue">
|
|
|
+ <el-input-number v-model="form.targetValue" placeholder="请输入目标值" :min="0" :precision="0"
|
|
|
+ controls-position="right"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="偏差" prop="toleranceRange">
|
|
|
+ <div style="display: flex; align-items: center;">
|
|
|
+ <span style="margin-right: 5px;">±</span>
|
|
|
+ <el-input-number v-model="form.toleranceRange" placeholder="" :min="0" :precision="0"
|
|
|
+ controls-position="right" style="flex: 1;"/>
|
|
|
+ <span style="margin-left: 5px;">%</span>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <!-- <el-form-item label="测量值" prop="measuredValue">-->
|
|
|
+ <!-- <el-input v-model="form.measuredValue" type="textarea" placeholder="测量值将通过子表录入并自动转换为JSON数组" :disabled="true" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="上限过程能力指数CPU" prop="cpuValue">-->
|
|
|
+ <!-- <el-input v-model="form.cpuValue" placeholder="请输入上限过程能力指数CPU" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="下限过程能力指数CPL" prop="cplValue">-->
|
|
|
+ <!-- <el-input v-model="form.cplValue" placeholder="请输入下限过程能力指数CPL" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="实际过程能力指数CPK" prop="cpkValue">-->
|
|
|
+ <!-- <el-input v-model="form.cpkValue" placeholder="请输入实际过程能力指数CPK" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="标准差" prop="stdDeviation">-->
|
|
|
+ <!-- <el-input v-model="form.stdDeviation" placeholder="请输入标准差" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="平均值" prop="meanValue">-->
|
|
|
+ <!-- <el-input v-model="form.meanValue" placeholder="请输入平均值" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="最大值" prop="maxValue">-->
|
|
|
+ <!-- <el-input v-model="form.maxValue" placeholder="请输入最大值" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="最小值" prop="minValue">-->
|
|
|
+ <!-- <el-input v-model="form.minValue" placeholder="请输入最小值" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="规格上限" prop="usl">-->
|
|
|
+ <!-- <el-input v-model="form.usl" placeholder="请输入规格上限" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="规格下限" prop="lsl">-->
|
|
|
+ <!-- <el-input v-model="form.lsl" placeholder="请输入规格下限" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+ <!-- <el-form-item label="备注" prop="remark">-->
|
|
|
+ <!-- <el-input v-model="form.remark" placeholder="请输入备注" />-->
|
|
|
+ <!-- </el-form-item>-->
|
|
|
+
|
|
|
+ <!-- 子表录入测量值 -->
|
|
|
+ <el-divider content-position="center">测量值录入</el-divider>
|
|
|
+ <div style="margin-bottom: 10px;">
|
|
|
+ <el-button type="primary" icon="Plus" @click="handleAddMeasuredValueGroup">添加测量值(每行10个)</el-button>
|
|
|
+ <el-button type="danger" icon="Delete" @click="handleDeleteMeasuredValue">删除选中</el-button>
|
|
|
+ </div>
|
|
|
+ <el-table :data="measuredValueList" :row-class-name="rowMeasuredValueIndex"
|
|
|
+ @selection-change="handleMeasuredValueSelectionChange" ref="measuredValueTable"
|
|
|
+ style="height: 430px; overflow-y: auto;">
|
|
|
+ <el-table-column type="selection" width="40" align="center"/>
|
|
|
+ <el-table-column label="测量值1" prop="value1">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value1"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值2" prop="value2">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value2"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值3" prop="value3">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value3"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值4" prop="value4">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value4"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值5" prop="value5">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value5"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值6" prop="value6">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value6"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值7" prop="value7">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value7"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值8" prop="value8">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value8"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值9" prop="value9">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value9"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="测量值10" prop="value10">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input-number
|
|
|
+ v-model="scope.row.value10"
|
|
|
+ placeholder="请输入"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="handleMeasuredValueChanged"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="60" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ icon="Delete"
|
|
|
+ @click="deleteSingleMeasuredValue(scope.$index)"
|
|
|
+ circle
|
|
|
+ size="small"
|
|
|
+ ></el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
+ <el-button @click="cancel">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 查看白坯布重量分析对话框 -->
|
|
|
+ <el-dialog :title="title" v-model="viewOpen" fullscreen append-to-body>
|
|
|
+ <div style="height: 100%; display: flex; flex-direction: column;">
|
|
|
+ <!-- 图表和信息区域 -->
|
|
|
+ <div style="height: 300px; flex-shrink: 0; margin-bottom: 15px;">
|
|
|
+ <el-row :gutter="15" style="height: 100%;">
|
|
|
+ <el-col :span="16" style="height: 100%;">
|
|
|
+ <div style="width: 100%; height: 100%;" ref="chartContainerRef"></div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" style="height: 100%; display: flex; flex-direction: column;">
|
|
|
+ <div style="height: 45%; margin-bottom: 15px;">
|
|
|
+ <el-descriptions title="基本信息" :column="2" border size="small" style="height: 100%;">
|
|
|
+ <el-descriptions-item label="测量时间">{{ parseTime(viewForm.dataTime, '{y}-{m}-{d}') }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="目标值">{{ viewForm.targetValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="误差范围">±{{ viewForm.toleranceRange }}%</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="误差值">{{ viewForm.toleranceValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规格上限">{{ viewForm.usl }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规格下限">{{ viewForm.lsl }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+ <div style="height: 55%;">
|
|
|
+ <el-descriptions title="统计信息" :column="2" border size="small" style="height: 100%;">
|
|
|
+ <el-descriptions-item label="平均值">{{ viewForm.meanValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="最大值">{{ viewForm.maxValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="最小值">{{ viewForm.minValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="标准差">{{ viewForm.stdDeviation }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="上限过程能力指数CPU">{{ viewForm.cpuValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="下限过程能力指数CPL">{{ viewForm.cplValue }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="实际过程能力指数CPK" :span="2">
|
|
|
+ {{ viewForm.cpkValue }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 表格区域 -->
|
|
|
+ <div style="flex: 1; overflow: hidden;">
|
|
|
+ <el-row :gutter="15" style="height: 100%;">
|
|
|
+ <el-col :span="10" style="height: 100%; display: flex; flex-direction: column;">
|
|
|
+ <div style="font-size: 14px; font-weight: bold; margin-bottom: 10px;">CPK过程能力评价标准</div>
|
|
|
+ <div style="flex: 1; overflow: auto;">
|
|
|
+ <el-table :data="cpkEvaluation" border :row-class-name="cpkTableRowClassName" size="mini" style="font-size: 12px;">
|
|
|
+ <el-table-column prop="cpk" label="Cpk值" ></el-table-column>
|
|
|
+ <el-table-column prop="level" label="等级" width="70"></el-table-column>
|
|
|
+ <el-table-column prop="evaluation" label="过程能力评价"></el-table-column>
|
|
|
+ <el-table-column prop="defectRate" label="大约对应不良率"></el-table-column>
|
|
|
+ <el-table-column prop="description" label="说明" width="320"></el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="14" style="height: 100%; display: flex; flex-direction: column;">
|
|
|
+ <div style="font-size: 14px; font-weight: bold; margin-bottom: 10px;">标准差水平评估标准</div>
|
|
|
+ <div style="flex: 1; overflow: auto;">
|
|
|
+ <el-table :data="calculatedStdDeviationEvaluation" border :row-class-name="stdDeviationTableRowClassName" size="mini" style="font-size: 12px;">
|
|
|
+ <el-table-column prop="level" label="标准差水平"></el-table-column>
|
|
|
+ <el-table-column prop="stdValue" label="标准差数值"></el-table-column>
|
|
|
+ <el-table-column prop="cpk" label="Cpk值" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="evaluation" label="过程能力评价"></el-table-column>
|
|
|
+ <el-table-column prop="stability" label="稳定性状态"></el-table-column>
|
|
|
+ <el-table-column prop="defectRate" label="大约不良率"></el-table-column>
|
|
|
+ <el-table-column prop="description" label="含义与行动建议" width="420"></el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="viewOpen = false">关 闭</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="Cap">
|
|
|
+import * as echarts from 'echarts';
|
|
|
+import {addCap, delCap, getCap, listCap, updateCap} from "@/api/lean/cap";
|
|
|
+
|
|
|
+const {proxy} = getCurrentInstance();
|
|
|
+
|
|
|
+const capList = ref([]);
|
|
|
+const open = ref(false);
|
|
|
+const viewOpen = ref(false); // 查看详情弹窗
|
|
|
+const chartContainerRef = ref(null); // 图表容器引用
|
|
|
+let chartInstance = null; // 保存图表实例
|
|
|
+const loading = ref(true);
|
|
|
+const showSearch = ref(true);
|
|
|
+const ids = ref([]);
|
|
|
+const single = ref(true);
|
|
|
+const multiple = ref(true);
|
|
|
+const total = ref(0);
|
|
|
+const title = ref("");
|
|
|
+// 子表相关变量
|
|
|
+const measuredValueList = ref([]);
|
|
|
+const checkedMeasuredValues = ref([]);
|
|
|
+// 查看详情相关变量
|
|
|
+const viewForm = ref({});
|
|
|
+const measuredValues = ref([]); // 解析后的测量值数组
|
|
|
+
|
|
|
+const data = reactive({
|
|
|
+ form: {},
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ dataTime: null,
|
|
|
+ targetValue: null,
|
|
|
+ toleranceRange: null,
|
|
|
+ toleranceValue: null,
|
|
|
+ measuredValue: null,
|
|
|
+ cpuValue: null,
|
|
|
+ cplValue: null,
|
|
|
+ cpkValue: null,
|
|
|
+ stdDeviation: null,
|
|
|
+ meanValue: null,
|
|
|
+ maxValue: null,
|
|
|
+ minValue: null,
|
|
|
+ usl: null,
|
|
|
+ lsl: null,
|
|
|
+ createBy: null,
|
|
|
+ createTime: null,
|
|
|
+ updateBy: null,
|
|
|
+ updateTime: null,
|
|
|
+ remark: null
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ dataTime: [
|
|
|
+ {required: true, message: "测量时间不能为空", trigger: "blur"}
|
|
|
+ ],
|
|
|
+ targetValue: [
|
|
|
+ {required: true, message: "目标值不能为空", trigger: "blur"}
|
|
|
+ ],
|
|
|
+ toleranceRange: [
|
|
|
+ {required: true, message: "误差范围不能为空", trigger: "blur"}
|
|
|
+ ],
|
|
|
+ measuredValue: [
|
|
|
+ {required: true, message: "测量值不能为空", trigger: "blur"}
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ // CPK评价标准数据
|
|
|
+ cpkEvaluation: [
|
|
|
+ {
|
|
|
+ cpk: "Cpk < 1.0",
|
|
|
+ level: "不足",
|
|
|
+ evaluation: "过程能力严重不足",
|
|
|
+ defectRate: "> 2700 PPM",
|
|
|
+ description: "不可接受。必须立即改进。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ cpk: "1.0 ≤ Cpk < 1.33",
|
|
|
+ level: "尚可",
|
|
|
+ evaluation: "过程能力尚可",
|
|
|
+ defectRate: "~ 63 PPM",
|
|
|
+ description: "需要加强监控和改善。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ cpk: "1.33 ≤ Cpk < 1.67",
|
|
|
+ level: "良好",
|
|
|
+ evaluation: "过程能力充足",
|
|
|
+ defectRate: "~ 0.57 PPM",
|
|
|
+ description: "对于大多数行业而言,这是一个良好且可接受的目标。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ cpk: "Cpk ≥ 1.67",
|
|
|
+ level: "优秀",
|
|
|
+ evaluation: "过程能力很高",
|
|
|
+ defectRate: "~ 0.00034 PPM",
|
|
|
+ description: "达到“六西格玛”水平的标志。世界级制造水平。"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+});
|
|
|
+
|
|
|
+const {queryParams, form, rules, cpkEvaluation} = toRefs(data);
|
|
|
+
|
|
|
+// 标准差评价标准数据
|
|
|
+const stdDeviationEvaluation = [
|
|
|
+ {
|
|
|
+ level: "世界级",
|
|
|
+ stdValue: "7.5 克",
|
|
|
+ cpk: "2.0",
|
|
|
+ evaluation: "过剩",
|
|
|
+ stability: "极其稳定",
|
|
|
+ defectRate: "~ 0.001 PPM",
|
|
|
+ description: "过程波动仅占公差带的17%。稳定性极佳,可考虑缩小公差或降低成本。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "优秀",
|
|
|
+ stdValue: "~ 11.3 克",
|
|
|
+ cpk: "1.33",
|
|
|
+ evaluation: "充足",
|
|
|
+ stability: "高度稳定",
|
|
|
+ defectRate: "~ 63 PPM",
|
|
|
+ description: "行业优秀水平。波动可控,过程可靠,是优质企业追求的目标。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "最低可接受",
|
|
|
+ stdValue: "15 克",
|
|
|
+ cpk: "1.0",
|
|
|
+ evaluation: "勉强达标",
|
|
|
+ stability: "基本稳定",
|
|
|
+ defectRate: "~ 2700 PPM",
|
|
|
+ description: "过程波动占公差带的1/3。如履薄冰,需严密监控,否则极易出界。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "不足",
|
|
|
+ stdValue: "22.5 克",
|
|
|
+ cpk: "~ 0.67",
|
|
|
+ evaluation: "不足",
|
|
|
+ stability: "不稳定",
|
|
|
+ defectRate: "~ 45500 PPM",
|
|
|
+ description: "波动已占公差带的一半。必须采取措施,改进过程。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "严重不足",
|
|
|
+ stdValue: "30 克",
|
|
|
+ cpk: "0.5",
|
|
|
+ evaluation: "严重不足",
|
|
|
+ stability: "极度不稳定",
|
|
|
+ defectRate: "> 66000 PPM",
|
|
|
+ description: "过程波动极大,失控状态。产品质量无法保证,急需全面整改。"
|
|
|
+ }
|
|
|
+];
|
|
|
+
|
|
|
+/** 查询白坯布重量分析列表 */
|
|
|
+function getList() {
|
|
|
+ loading.value = true;
|
|
|
+ listCap(queryParams.value).then(response => {
|
|
|
+ capList.value = response.rows;
|
|
|
+ total.value = response.total;
|
|
|
+ loading.value = false;
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 取消按钮
|
|
|
+function cancel() {
|
|
|
+ open.value = false;
|
|
|
+ reset();
|
|
|
+}
|
|
|
+
|
|
|
+// 表单重置
|
|
|
+function reset() {
|
|
|
+ form.value = {
|
|
|
+ bwId: null,
|
|
|
+ dataTime: null,
|
|
|
+ dataType: null,
|
|
|
+ targetValue: null,
|
|
|
+ toleranceRange: null,
|
|
|
+ toleranceValue: null,
|
|
|
+ measuredValue: null,
|
|
|
+ cpuValue: null,
|
|
|
+ cplValue: null,
|
|
|
+ cpkValue: null,
|
|
|
+ stdDeviation: null,
|
|
|
+ meanValue: null,
|
|
|
+ maxValue: null,
|
|
|
+ minValue: null,
|
|
|
+ usl: null,
|
|
|
+ lsl: null,
|
|
|
+ createBy: null,
|
|
|
+ createTime: null,
|
|
|
+ updateBy: null,
|
|
|
+ updateTime: null,
|
|
|
+ remark: null
|
|
|
+ };
|
|
|
+ measuredValueList.value = [];
|
|
|
+ proxy.resetForm("capRef");
|
|
|
+}
|
|
|
+
|
|
|
+/** 搜索按钮操作 */
|
|
|
+function handleQuery() {
|
|
|
+ queryParams.value.pageNum = 1;
|
|
|
+ getList();
|
|
|
+}
|
|
|
+
|
|
|
+/** 重置按钮操作 */
|
|
|
+function resetQuery() {
|
|
|
+ proxy.resetForm("queryRef");
|
|
|
+ handleQuery();
|
|
|
+}
|
|
|
+
|
|
|
+// 多选框选中数据
|
|
|
+function handleSelectionChange(selection) {
|
|
|
+ ids.value = selection.map(item => item.bwId);
|
|
|
+ single.value = selection.length != 1;
|
|
|
+ multiple.value = !selection.length;
|
|
|
+}
|
|
|
+
|
|
|
+/** 新增按钮操作 */
|
|
|
+function handleAdd() {
|
|
|
+ reset();
|
|
|
+ open.value = true;
|
|
|
+ title.value = "添加白坯布重量分析";
|
|
|
+}
|
|
|
+
|
|
|
+/** 修改按钮操作 */
|
|
|
+function handleUpdate(row) {
|
|
|
+ reset();
|
|
|
+ const _bwId = row.bwId || ids.value;
|
|
|
+ getCap(_bwId).then(response => {
|
|
|
+ form.value = response.data;
|
|
|
+ // 解析测量值JSON数组
|
|
|
+ try {
|
|
|
+ if (form.value.measuredValue) {
|
|
|
+ // 支持两种格式:简单数组格式 [1,2,3,4,5] 和对象数组格式 [{value: 1}, {value: 2}]
|
|
|
+ const parsed = JSON.parse(form.value.measuredValue);
|
|
|
+ if (Array.isArray(parsed)) {
|
|
|
+ if (parsed.length > 0 && typeof parsed[0] === 'object') {
|
|
|
+ // 对象数组格式 [{value: 1}, {value: 2}]
|
|
|
+ // 转换为每行10个的格式
|
|
|
+ measuredValueList.value = [];
|
|
|
+ for (let i = 0; i < parsed.length; i += 10) {
|
|
|
+ let obj = {};
|
|
|
+ for (let j = 1; j <= 10; j++) {
|
|
|
+ if (parsed[i + j - 1] !== undefined) {
|
|
|
+ obj[`value${j}`] = parsed[i + j - 1].value || parsed[i + j - 1];
|
|
|
+ } else {
|
|
|
+ obj[`value${j}`] = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ measuredValueList.value.push(obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存解析后的测量值用于查看
|
|
|
+ measuredValues.value = parsed.map(item =>
|
|
|
+ typeof item === 'object' ? item.value : item
|
|
|
+ );
|
|
|
+ } else if (parsed.length > 0 && typeof parsed[0] === 'number') {
|
|
|
+ // 简单数组格式 [1,2,3,4,5]
|
|
|
+ // 转换为每行10个的格式
|
|
|
+ measuredValueList.value = [];
|
|
|
+ for (let i = 0; i < parsed.length; i += 10) {
|
|
|
+ let obj = {};
|
|
|
+ for (let j = 1; j <= 10; j++) {
|
|
|
+ obj[`value${j}`] = parsed[i + j - 1] !== undefined ? parsed[i + j - 1] : null;
|
|
|
+ }
|
|
|
+ measuredValueList.value.push(obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存解析后的测量值用于查看
|
|
|
+ measuredValues.value = [...parsed];
|
|
|
+ } else {
|
|
|
+ // 空数组或其他情况
|
|
|
+ measuredValueList.value = [];
|
|
|
+ measuredValues.value = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error("解析测量值JSON失败", e);
|
|
|
+ measuredValueList.value = [];
|
|
|
+ measuredValues.value = [];
|
|
|
+ }
|
|
|
+ open.value = true;
|
|
|
+ title.value = "修改白坯布重量分析";
|
|
|
+
|
|
|
+ // 加载数据后计算统计值
|
|
|
+ nextTick(() => {
|
|
|
+ calculateStatistics();
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 查看按钮操作 */
|
|
|
+function handleView(row) {
|
|
|
+ viewForm.value = {...row};
|
|
|
+
|
|
|
+ // 解析测量值JSON数组
|
|
|
+ try {
|
|
|
+ if (viewForm.value.measuredValue) {
|
|
|
+ const parsed = JSON.parse(viewForm.value.measuredValue);
|
|
|
+ if (Array.isArray(parsed)) {
|
|
|
+ if (parsed.length > 0 && typeof parsed[0] === 'object') {
|
|
|
+ // 对象数组格式 [{value: 1}, {value: 2}]
|
|
|
+ measuredValues.value = parsed.map(item =>
|
|
|
+ typeof item === 'object' ? item.value : item
|
|
|
+ );
|
|
|
+ } else if (parsed.length > 0 && typeof parsed[0] === 'number') {
|
|
|
+ // 简单数组格式 [1,2,3,4,5]
|
|
|
+ measuredValues.value = [...parsed];
|
|
|
+ } else {
|
|
|
+ measuredValues.value = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error("解析测量值JSON失败", e);
|
|
|
+ measuredValues.value = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ viewOpen.value = true;
|
|
|
+ title.value = "查看白坯布重量分析";
|
|
|
+
|
|
|
+ // 在弹窗打开后绘制图表
|
|
|
+ nextTick(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ drawChart();
|
|
|
+ }, 100);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 提交按钮 */
|
|
|
+function submitForm() {
|
|
|
+ proxy.$refs["capRef"].validate(valid => {
|
|
|
+ if (valid) {
|
|
|
+ // 计算误差值:目标值*(1+误差范围%) - 目标值*(1-误差范围%)
|
|
|
+ if (form.value.targetValue !== null && form.value.toleranceRange !== null) {
|
|
|
+ const upperLimit = form.value.targetValue * (1 + form.value.toleranceRange / 100);
|
|
|
+ const lowerLimit = form.value.targetValue * (1 - form.value.toleranceRange / 100);
|
|
|
+ form.value.toleranceValue = (upperLimit - lowerLimit).toFixed(4);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将测量值子表数据转换为简单数组格式 [1,2,3,4,5]
|
|
|
+ let simpleArray = [];
|
|
|
+
|
|
|
+ // 遍历所有行,收集所有非空的测量值
|
|
|
+ measuredValueList.value.forEach(row => {
|
|
|
+ for (let i = 1; i <= 10; i++) {
|
|
|
+ const value = row[`value${i}`];
|
|
|
+ if (value !== null && value !== undefined) {
|
|
|
+ simpleArray.push(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ form.value.measuredValue = JSON.stringify(simpleArray);
|
|
|
+
|
|
|
+ if (form.value.bwId != null) {
|
|
|
+ updateCap(form.value).then(response => {
|
|
|
+ proxy.$modal.msgSuccess("修改成功");
|
|
|
+ open.value = false;
|
|
|
+ getList();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ addCap(form.value).then(response => {
|
|
|
+ proxy.$modal.msgSuccess("新增成功");
|
|
|
+ open.value = false;
|
|
|
+ getList();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除按钮操作 */
|
|
|
+function handleDelete(row) {
|
|
|
+ const _bwIds = row.bwId || ids.value;
|
|
|
+ proxy.$modal.confirm('是否确认删除白坯布重量分析编号为"' + _bwIds + '"的数据项?').then(function () {
|
|
|
+ return delCap(_bwIds);
|
|
|
+ }).then(() => {
|
|
|
+ getList();
|
|
|
+ proxy.$modal.msgSuccess("删除成功");
|
|
|
+ }).catch(() => {
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 导出按钮操作 */
|
|
|
+function handleExport() {
|
|
|
+ proxy.download('lean/cap/export', {
|
|
|
+ ...queryParams.value
|
|
|
+ }, `cap_${new Date().getTime()}.xlsx`)
|
|
|
+}
|
|
|
+
|
|
|
+// 子表相关函数
|
|
|
+/** 测量值序号 */
|
|
|
+function rowMeasuredValueIndex({row, rowIndex}) {
|
|
|
+ row.index = rowIndex + 1;
|
|
|
+}
|
|
|
+
|
|
|
+/** 测量值添加按钮操作 */
|
|
|
+function handleAddMeasuredValue() {
|
|
|
+ let obj = {};
|
|
|
+ obj.value = null;
|
|
|
+ obj.remark = "";
|
|
|
+ measuredValueList.value.push(obj);
|
|
|
+}
|
|
|
+
|
|
|
+/** 测量值添加按钮操作(每行10个) */
|
|
|
+function handleAddMeasuredValueGroup() {
|
|
|
+ let obj = {};
|
|
|
+ // 使用目标值作为默认值,如果目标值为空则使用null
|
|
|
+ const defaultValue = form.value.targetValue !== null ? form.value.targetValue : null;
|
|
|
+ obj.value1 = defaultValue;
|
|
|
+ obj.value2 = defaultValue;
|
|
|
+ obj.value3 = defaultValue;
|
|
|
+ obj.value4 = defaultValue;
|
|
|
+ obj.value5 = defaultValue;
|
|
|
+ obj.value6 = defaultValue;
|
|
|
+ obj.value7 = defaultValue;
|
|
|
+ obj.value8 = defaultValue;
|
|
|
+ obj.value9 = defaultValue;
|
|
|
+ obj.value10 = defaultValue;
|
|
|
+ measuredValueList.value.push(obj);
|
|
|
+}
|
|
|
+
|
|
|
+/** 测量值删除按钮操作 */
|
|
|
+function handleDeleteMeasuredValue() {
|
|
|
+ if (checkedMeasuredValues.value.length == 0) {
|
|
|
+ proxy.$modal.msgError("请先选择要删除的测量值数据");
|
|
|
+ } else {
|
|
|
+ const measuredValues = measuredValueList.value;
|
|
|
+ const checkedValues = checkedMeasuredValues.value;
|
|
|
+ measuredValueList.value = measuredValues.filter(function (item) {
|
|
|
+ return checkedValues.indexOf(item.index) == -1
|
|
|
+ });
|
|
|
+
|
|
|
+ // 删除后重新计算统计值
|
|
|
+ calculateStatistics();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除单个测量值 */
|
|
|
+function deleteSingleMeasuredValue(index) {
|
|
|
+ measuredValueList.value.splice(index, 1);
|
|
|
+ // 重新计算统计值
|
|
|
+ calculateStatistics();
|
|
|
+}
|
|
|
+
|
|
|
+// 根据CPK值确定行的类名
|
|
|
+function tableRowClassName({row, rowIndex}) {
|
|
|
+ return '';
|
|
|
+}
|
|
|
+
|
|
|
+// CPK评价表格行类名
|
|
|
+function cpkTableRowClassName({row, rowIndex}) {
|
|
|
+ // 根据当前viewForm中的cpkValue值来确定应该高亮哪一行
|
|
|
+ if (viewForm.value.cpkValue !== null && viewForm.value.cpkValue !== undefined) {
|
|
|
+ const cpkValue = parseFloat(viewForm.value.cpkValue);
|
|
|
+
|
|
|
+ if (cpkValue < 1.0 && rowIndex === 0) {
|
|
|
+ return 'cpk-current-row'; // Cpk < 1.0 对应第一行
|
|
|
+ } else if (cpkValue >= 1.0 && cpkValue < 1.33 && rowIndex === 1) {
|
|
|
+ return 'cpk-current-row'; // 1.0 ≤ Cpk < 1.33 对应第二行
|
|
|
+ } else if (cpkValue >= 1.33 && cpkValue < 1.67 && rowIndex === 2) {
|
|
|
+ return 'cpk-current-row'; // 1.33 ≤ Cpk < 1.67 对应第三行
|
|
|
+ } else if (cpkValue >= 1.67 && rowIndex === 3) {
|
|
|
+ return 'cpk-current-row'; // Cpk ≥ 1.67 对应第四行
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+}
|
|
|
+
|
|
|
+// 标准差评价表格行类名
|
|
|
+function stdDeviationTableRowClassName({row, rowIndex}) {
|
|
|
+ // 根据当前viewForm中的stdDeviation值来确定应该高亮哪一行
|
|
|
+ if (viewForm.value.stdDeviation !== null && viewForm.value.stdDeviation !== undefined) {
|
|
|
+ const stdDeviation = parseFloat(viewForm.value.stdDeviation);
|
|
|
+
|
|
|
+ // 获取各个等级的标准差阈值
|
|
|
+ const worldClassLimit = parseFloat(calculateStdDevByCpk("2.0"));
|
|
|
+ const excellentLimit = parseFloat(calculateStdDevByCpk("1.33"));
|
|
|
+ const acceptableLimit = parseFloat(calculateStdDevByCpk("1.0"));
|
|
|
+ const insufficientLimit = parseFloat(calculateStdDevByCpk("0.67"));
|
|
|
+
|
|
|
+ // 确定当前标准差值对应的等级行
|
|
|
+ if (stdDeviation <= worldClassLimit && rowIndex === 0) {
|
|
|
+ return 'cpk-current-row'; // 世界级对应第一行
|
|
|
+ } else if (stdDeviation > worldClassLimit && stdDeviation <= excellentLimit && rowIndex === 1) {
|
|
|
+ return 'cpk-current-row'; // 优秀级对应第二行
|
|
|
+ } else if (stdDeviation > excellentLimit && stdDeviation <= acceptableLimit && rowIndex === 2) {
|
|
|
+ return 'cpk-current-row'; // 最低可接受对应第三行
|
|
|
+ } else if (stdDeviation > acceptableLimit && stdDeviation <= insufficientLimit && rowIndex === 3) {
|
|
|
+ return 'cpk-current-row'; // 不足对应第四行
|
|
|
+ } else if (stdDeviation > insufficientLimit && rowIndex === 4) {
|
|
|
+ return 'cpk-current-row'; // 严重不足对应第五行
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+}
|
|
|
+
|
|
|
+/** 测量值复选框选中数据 */
|
|
|
+function handleMeasuredValueSelectionChange(selection) {
|
|
|
+ checkedMeasuredValues.value = selection.map(item => item.index)
|
|
|
+}
|
|
|
+
|
|
|
+/** 测量值变化时自动计算统计值 */
|
|
|
+function handleMeasuredValueChanged() {
|
|
|
+ calculateStatistics();
|
|
|
+}
|
|
|
+
|
|
|
+/** 计算统计值 */
|
|
|
+function calculateStatistics() {
|
|
|
+ // 获取所有有效的测量值
|
|
|
+ let validValues = [];
|
|
|
+
|
|
|
+ // 遍历所有行,收集所有非空的测量值
|
|
|
+ measuredValueList.value.forEach(row => {
|
|
|
+ for (let i = 1; i <= 10; i++) {
|
|
|
+ const value = row[`value${i}`];
|
|
|
+ if (value !== null && value !== undefined) {
|
|
|
+ validValues.push(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (validValues.length === 0) {
|
|
|
+ // 如果没有有效值,清空统计字段
|
|
|
+ form.value.meanValue = null;
|
|
|
+ form.value.maxValue = null;
|
|
|
+ form.value.minValue = null;
|
|
|
+ form.value.stdDeviation = null;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算平均值
|
|
|
+ const mean = validValues.reduce((sum, value) => sum + value, 0) / validValues.length;
|
|
|
+ form.value.meanValue = mean.toFixed(4);
|
|
|
+
|
|
|
+ // 计算最大值和最小值
|
|
|
+ form.value.maxValue = Math.max(...validValues);
|
|
|
+ form.value.minValue = Math.min(...validValues);
|
|
|
+
|
|
|
+ // 计算标准差
|
|
|
+ const squaredDiffs = validValues.map(value => Math.pow(value - mean, 2));
|
|
|
+ const avgSquaredDiff = squaredDiffs.reduce((sum, value) => sum + value, 0) / validValues.length;
|
|
|
+ form.value.stdDeviation = Math.sqrt(avgSquaredDiff).toFixed(4);
|
|
|
+
|
|
|
+ // 如果目标值和误差范围已设置,计算过程能力指数
|
|
|
+ if (form.value.targetValue !== null && form.value.toleranceRange !== null) {
|
|
|
+ // 修复:误差范围是百分比,需要正确计算规格上下限
|
|
|
+ const usl = form.value.targetValue * (1 + form.value.toleranceRange / 100); // 规格上限
|
|
|
+ const lsl = form.value.targetValue * (1 - form.value.toleranceRange / 100); // 规格下限
|
|
|
+
|
|
|
+ form.value.usl = usl.toFixed(4);
|
|
|
+ form.value.lsl = lsl.toFixed(4);
|
|
|
+
|
|
|
+ // 计算过程能力指数
|
|
|
+ const stdDev = parseFloat(form.value.stdDeviation);
|
|
|
+ if (stdDev !== 0) {
|
|
|
+ // CPU = (USL - mean) / (3 * σ)
|
|
|
+ form.value.cpuValue = ((usl - mean) / (3 * stdDev)).toFixed(4);
|
|
|
+ // CPL = (mean - LSL) / (3 * σ)
|
|
|
+ form.value.cplValue = ((mean - lsl) / (3 * stdDev)).toFixed(4);
|
|
|
+ // CPK = min(CPU, CPL)
|
|
|
+ form.value.cpkValue = (Math.min(parseFloat(form.value.cpuValue), parseFloat(form.value.cplValue))).toFixed(4);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 监听目标值和误差范围变化
|
|
|
+watch(() => [form.value.targetValue, form.value.toleranceRange], () => {
|
|
|
+ calculateStatistics();
|
|
|
+});
|
|
|
+
|
|
|
+// 绘制直方图
|
|
|
+function drawChart() {
|
|
|
+ const chartDom = chartContainerRef.value;
|
|
|
+ if (!chartDom) {
|
|
|
+ console.error('图表容器未找到');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已有图表实例,先销毁
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空容器
|
|
|
+ chartDom.innerHTML = '';
|
|
|
+
|
|
|
+ chartInstance = echarts.init(chartDom);
|
|
|
+
|
|
|
+ // 准备数据
|
|
|
+ const values = measuredValues.value.filter(val => val !== null && val !== undefined);
|
|
|
+ if (values.length === 0) {
|
|
|
+ console.warn('没有有效的测量值数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算基本统计信息
|
|
|
+ const minValue = Math.min(...values);
|
|
|
+ const maxValue = Math.max(...values);
|
|
|
+ const range = maxValue - minValue;
|
|
|
+
|
|
|
+ // 计算均值和标准差
|
|
|
+ const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
|
+ const squaredDiffs = values.map(val => Math.pow(val - mean, 2));
|
|
|
+ const avgSquaredDiff = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;
|
|
|
+ const stdDev = Math.sqrt(avgSquaredDiff);
|
|
|
+
|
|
|
+ // 如果所有值都相同,则创建一个简单的柱状图
|
|
|
+ if (range === 0) {
|
|
|
+ const option = {
|
|
|
+ title: {
|
|
|
+ text: '测量值分布直方图'
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: 'category',
|
|
|
+ data: [minValue.toFixed(2)],
|
|
|
+ axisTick: {
|
|
|
+ alignWithLabel: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '频次'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '测量值',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: '60%',
|
|
|
+ data: [values.length],
|
|
|
+ itemStyle: {
|
|
|
+ color: '#409EFF'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ chartInstance.setOption(option);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 正常情况下的直方图和正态分布曲线
|
|
|
+ const interval = range / 10;
|
|
|
+
|
|
|
+ // 构造区间和频次
|
|
|
+ const bins = [];
|
|
|
+ const counts = [];
|
|
|
+ const binCenters = []; // 用于正态分布曲线的x轴
|
|
|
+
|
|
|
+ for (let i = 0; i < 10; i++) {
|
|
|
+ const start = minValue + i * interval;
|
|
|
+ const end = i === 9 ? maxValue : minValue + (i + 1) * interval;
|
|
|
+ bins.push(`${start.toFixed(2)}-${end.toFixed(2)}`);
|
|
|
+ binCenters.push((start + end) / 2); // 区间中点
|
|
|
+
|
|
|
+ const count = values.filter(val => val >= start && val < end).length;
|
|
|
+ // 最后一个区间包含最大值
|
|
|
+ if (i === 9) {
|
|
|
+ const maxCount = values.filter(val => val === maxValue).length;
|
|
|
+ counts.push(count + maxCount);
|
|
|
+ } else {
|
|
|
+ counts.push(count);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算正态分布曲线的y值
|
|
|
+ const normalY = binCenters.map(x => {
|
|
|
+ // 正态分布概率密度函数
|
|
|
+ return (1 / (Math.sqrt(2 * Math.PI) * stdDev)) *
|
|
|
+ Math.exp(-0.5 * Math.pow((x - mean) / stdDev, 2));
|
|
|
+ });
|
|
|
+
|
|
|
+ // 将概率密度转换为与直方图可比较的频次
|
|
|
+ const maxValueCount = Math.max(...counts);
|
|
|
+ const maxNormalY = Math.max(...normalY);
|
|
|
+ const normalCounts = normalY.map(y => (y / maxNormalY) * maxValueCount);
|
|
|
+
|
|
|
+ // 配置图表选项(参考calcMaogao中的实现)
|
|
|
+ const option = {
|
|
|
+ grid: {
|
|
|
+ top: "15%",
|
|
|
+ bottom: "15%",
|
|
|
+ left: "5%",
|
|
|
+ right: "5%",
|
|
|
+ },
|
|
|
+ title: {
|
|
|
+ show: true,
|
|
|
+ text: "测量值分布直方图与正态分布曲线",
|
|
|
+ left: "center",
|
|
|
+ top: "0",
|
|
|
+ },
|
|
|
+ color: ['#409EFF', '#ff8896'],
|
|
|
+ legend: {
|
|
|
+ show: true,
|
|
|
+ top: "30",
|
|
|
+ itemGap: 50
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: bins,
|
|
|
+ axisTick: {
|
|
|
+ alignWithLabel: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ name: '频次'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "测量值分布",
|
|
|
+ data: counts,
|
|
|
+ type: "bar",
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#409EFF'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "正态分布",
|
|
|
+ data: normalCounts,
|
|
|
+ type: "line",
|
|
|
+ smooth: true,
|
|
|
+ symbol: 'none',
|
|
|
+ yAxisIndex: 0,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ff8896'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ chartInstance.setOption(option);
|
|
|
+
|
|
|
+ // 监听窗口大小变化,自适应调整图表大小
|
|
|
+ const handleResize = () => {
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.resize();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ window.addEventListener('resize', handleResize);
|
|
|
+
|
|
|
+ // 监听弹窗打开事件,确保图表正确显示
|
|
|
+ setTimeout(() => {
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.resize();
|
|
|
+ }
|
|
|
+ }, 300);
|
|
|
+
|
|
|
+ // 在组件销毁前清理事件监听器
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', handleResize);
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.dispose();
|
|
|
+ chartInstance = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 监听查看弹窗的开启和关闭
|
|
|
+watch(() => viewOpen.value, (newVal) => {
|
|
|
+ if (!newVal) {
|
|
|
+ // 弹窗关闭时,清理可能存在的图表实例
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.dispose();
|
|
|
+ chartInstance = null;
|
|
|
+ }
|
|
|
+ nextTick(() => {
|
|
|
+ if (chartContainerRef.value) {
|
|
|
+ chartContainerRef.value.innerHTML = '';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+getList();
|
|
|
+
|
|
|
+// 计算标准差水平评估数据
|
|
|
+const calculatedStdDeviationEvaluation = computed(() => {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ level: "世界级",
|
|
|
+ stdValue: "s ≤ " + calculateStdDevByCpk("2.0"),
|
|
|
+ cpk: "≥ 2.0",
|
|
|
+ evaluation: "过剩",
|
|
|
+ stability: "极其稳定",
|
|
|
+ defectRate: "~ 0.001 PPM",
|
|
|
+ description: "过程波动仅占公差带的17%。稳定性极佳,可考虑缩小公差或降低成本。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "优秀",
|
|
|
+ stdValue: calculateStdDevByCpk("2.0") + " < s ≤ " + calculateStdDevByCpk("1.33"),
|
|
|
+ cpk: "≥ 1.33",
|
|
|
+ evaluation: "充足",
|
|
|
+ stability: "高度稳定",
|
|
|
+ defectRate: "~ 63 PPM",
|
|
|
+ description: "行业优秀水平。波动可控,过程可靠,是优质企业追求的目标。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "最低可接受",
|
|
|
+ stdValue: calculateStdDevByCpk("1.33") + " < s ≤ " + calculateStdDevByCpk("1.0"),
|
|
|
+ cpk: "≥ 1.0",
|
|
|
+ evaluation: "勉强达标",
|
|
|
+ stability: "基本稳定",
|
|
|
+ defectRate: "~ 2700 PPM",
|
|
|
+ description: "过程波动占公差带的1/3。如履薄冰,需严密监控,否则极易出界。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "不足",
|
|
|
+ stdValue: calculateStdDevByCpk("1.0") + " < s ≤ " + calculateStdDevByCpk("0.67"),
|
|
|
+ cpk: "~ 0.67",
|
|
|
+ evaluation: "不足",
|
|
|
+ stability: "不稳定",
|
|
|
+ defectRate: "~ 45500 PPM",
|
|
|
+ description: "波动已占公差带的一半。必须采取措施,改进过程。"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ level: "严重不足",
|
|
|
+ stdValue: "s ≥ " + calculateStdDevByCpk("0.67"),
|
|
|
+ cpk: "≤ 0.5",
|
|
|
+ evaluation: "严重不足",
|
|
|
+ stability: "极度不稳定",
|
|
|
+ defectRate: "> 66000 PPM",
|
|
|
+ description: "过程波动极大,失控状态。产品质量无法保证,急需全面整改。"
|
|
|
+ }
|
|
|
+ ];
|
|
|
+});
|
|
|
+
|
|
|
+// 根据表格中的Cpk值计算标准差
|
|
|
+function calculateStdDevByCpk(tableCpkValue) {
|
|
|
+ // 根据标准差计算公式:标准差 = 误差值/(6*表格中的cpk值)
|
|
|
+ if (viewForm.value.toleranceValue && tableCpkValue) {
|
|
|
+ const toleranceValue = parseFloat(viewForm.value.toleranceValue);
|
|
|
+ const cpkValue = parseFloat(tableCpkValue);
|
|
|
+ if (cpkValue !== 0) {
|
|
|
+ return (toleranceValue / (6 * cpkValue)).toFixed(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0; // 返回默认值0而不是'N/A',便于数值比较
|
|
|
+}
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.cpk-current-row td {
|
|
|
+ background-color: rgba(255, 215, 0, 0.3) !important; /* 金色背景 */
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+</style>
|