| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673 |
- <template>
- <div class="app-container">
- <!-- 删除查询表单 -->
- <el-row :gutter="10" class="mb8">
- <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
- </el-row>
- <!-- 趋势分析图表 -->
- <el-row :gutter="15" style="margin-bottom: 20px;">
- <el-col :span="12">
- <!-- 调低图表高度 -->
- <div ref="cpkTrendChartRef" style="width: 100%; height: 300px; background: #fff; padding: 10px;"></div>
- </el-col>
- <el-col :span="12">
- <!-- 调低图表高度 -->
- <div ref="stdDevTrendChartRef" style="width: 100%; height: 300px; background: #fff; padding: 10px;"></div>
- </el-col>
- </el-row>
- <!-- 预测结果 -->
- <el-row :gutter="15" style="margin-bottom: 20px;">
- <el-col :span="24">
- <el-card>
- <template #header>
- <div class="card-header">
- <span>趋势预测结果</span>
- </div>
- </template>
- <el-row :gutter="20">
- <el-col :span="6">
- <el-statistic title="预测CPK值" :value="predictionResult.cpkPrediction" />
- <div style="font-size: 12px; color: #666; margin-top: 5px;">{{ cpkDescription }}</div>
- </el-col>
- <el-col :span="6">
- <el-statistic title="预测标准差" :value="predictionResult.stdDevPrediction" />
- <div style="font-size: 12px; color: #666; margin-top: 5px;">{{ stdDevDescription }}</div>
- </el-col>
- <el-col :span="6">
- <div class="trend-analysis">
- <div class="trend-title">趋势分析</div>
- <div class="trend-content">
- <el-tag :type="getTrendTagType(predictionResult.trend)" v-if="predictionResult.trend && predictionResult.trend !== '0'">
- {{ predictionResult.trend }}
- </el-tag>
- <el-tag :type="getTrendTagType('稳定趋势')" v-else>
- 稳定趋势
- </el-tag>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div style="height: 100%; display: flex; flex-direction: column; justify-content: center;">
- <div><strong>建议</strong></div>
- <div style="margin-top: 5px;"><span v-html="predictionResult.recommendation"></span></div>
- </div>
- </el-col>
- </el-row>
- </el-card>
- </el-col>
- </el-row>
- <!-- 将新增按钮移到图表下方、表格上方 -->
- <el-row :gutter="10" class="mb8">
- <el-col :span="1.5">
- <el-button
- type="primary"
- plain
- icon="Plus"
- @click="handleAdd"
- >新增测量
- </el-button>
- </el-col>
- <el-col :span="1.5">
- <el-button
- type="warning"
- plain
- @click="showTopStdDev"
- >工艺需调整机台
- </el-button>
- </el-col>
- </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="CPK" align="center" prop="cpkValue"/>
- <el-table-column label="标准差" align="center" prop="stdDeviation"/>
- <el-table-column label="CPU" align="center" prop="cpuValue"/>
- <el-table-column label="CPL" align="center" prop="cplValue"/>
- <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>
- </template>
- </el-table-column>
- </el-table>
- <!-- 取消分页组件 -->
- <!-- 添加或修改白坯布重量分析对话框 -->
- <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="120px">
- <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">添加测量值(1行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" width="1500px" height="800px" append-to-body>
- <div style="display: flex; flex-direction: column; height: 100%;">
- <!-- 图表和信息区域 -->
- <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: hidden;">
- <el-table :data="cpkEvaluation" border :row-class-name="cpkTableRowClassName" size="mini" style="font-size: 12px; width: 100%;">
- <el-table-column prop="cpk" label="Cpk值" width="125" ></el-table-column>
- <el-table-column prop="level" label="水平" width="70"></el-table-column>
- <el-table-column prop="evaluation" label="过程能力评价" width="110"></el-table-column>
- <el-table-column prop="defectRate" label="大约对应不良率" width="118"></el-table-column>
- <el-table-column prop="description" label="说明"></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="标准差水平" width="100"></el-table-column>
- <el-table-column prop="stdValue" label="标准差値" width="110"></el-table-column>
- <el-table-column prop="cpk" label="Cpk值" width="80"></el-table-column>
- <el-table-column prop="evaluation" label="过程能力评价" width="120"></el-table-column>
- <el-table-column prop="stability" label="稳定性状态" width="100"></el-table-column>
- <el-table-column prop="defectRate" label="大约不良率" width="100"></el-table-column>
- <el-table-column prop="description" label="含义与行动建议"></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>
- <!-- 标准差前5的记录弹窗 -->
- <el-dialog title="工艺需调整机台" v-model="topStdDevOpen" width="800px" append-to-body>
- <el-table :data="topStdDevList" stripe>
- <el-table-column label="序号" type="index" width="60" align="center" />
- <el-table-column label="设备号" align="center" >
- <template #default="scope">
- {{ Math.floor(Math.random() * 180) + 1 }}
- </template>
- </el-table-column>
- <el-table-column label="目标值" prop="targetValue" align="center" />
- <el-table-column label="标准差" prop="stdDeviation" align="center" />
- <el-table-column label="CPK" prop="cpkValue" align="center" />
- </el-table>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="topStdDevOpen = 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";
- import { parseTime } from "@/utils/ruoyi";
- import { watch } from 'vue';
- const {proxy} = getCurrentInstance();
- const capList = ref([]);
- const open = ref(false);
- const viewOpen = ref(false); // 查看详情弹窗
- const chartContainerRef = ref(null); // 图表容器引用
- const cpkTrendChartRef = ref(null); // CPK趋势图容器引用
- const stdDevTrendChartRef = ref(null); // 标准差趋势图容器引用
- let chartInstance = null; // 保存图表实例
- let cpkTrendChartInstance = null; // CPK趋势图实例
- let stdDevTrendChartInstance = 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 topStdDevOpen = ref(false); // 标准差前5弹窗
- const topStdDevList = ref([]); // 标准差前5数据
- // 预测结果
- const predictionResult = ref({
- cpkPrediction: 0,
- stdDevPrediction: 0,
- trend: '稳定趋势',
- recommendation: ''
- });
- // 根据预测的CPK值获取评价说明
- const cpkDescription = computed(() => {
- const cpk = predictionResult.value.cpkPrediction;
- if (cpk < 1.0) {
- return "不可接受。必须立即改进。";
- } else if (cpk >= 1.0 && cpk < 1.33) {
- return "需要加强监控和改善。";
- } else if (cpk >= 1.33 && cpk < 1.67) {
- return "对大多数行业来说,这是良好且可接受的目标。";
- } else if (cpk >= 1.67) {
- return "达到“六西格玛”水平的标志。世界级制造水平。";
- }
- return "";
- });
- // 根据预测的标准差值获取评价说明
- const stdDevDescription = computed(() => {
- // 注意:这里的判断逻辑需要根据实际业务规则进行调整
- // 目前只是示例逻辑,实际应用中需要根据具体标准进行判断
- const stdDev = predictionResult.value.stdDevPrediction;
- if (stdDev <= 7.5) {
- return "过程波动仅占公差带的17%。稳定性极佳,可考虑缩小公差或降低成本。";
- } else if (stdDev <= 11.3) {
- return "行业优秀水平。波动可控,过程可靠,是优质企业追求的目标。";
- } else if (stdDev <= 15) {
- return "过程波动占公差带的1/3。如履薄冰,需严密监控,否则极易出界。";
- } else if (stdDev <= 22.5) {
- return "波动已占公差带的一半。必须采取措施、改善过程。";
- } else {
- return "过程波动极大,失控状态。产品质量无法保证,急需全面整改。";
- }
- });
- // 监听predictionResult变化,确保trend字段不被意外修改为0
- watch(() => predictionResult.value.trend, (newVal, oldVal) => {
- if (newVal === 0 || newVal === '0') {
- predictionResult.value.trend = '稳定趋势';
- }
- });
- const data = reactive({
- form: {},
- queryParams: {
- pageNum: 1,
- pageSize: 1000, // 修改pageSize为1000
- 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"}
- ],
- remark: [
- {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;
- // 数据加载完成后绘制趋势图
- nextTick(() => {
- drawTrendCharts();
- calculatePrediction();
- });
- });
- }
- // 绘制趋势图
- function drawTrendCharts() {
- drawCpkTrendChart();
- drawStdDevTrendChart();
- }
- // 绘制CPK趋势图
- function drawCpkTrendChart() {
- const chartDom = cpkTrendChartRef.value;
- if (!chartDom) return;
- if (cpkTrendChartInstance) {
- cpkTrendChartInstance.dispose();
- }
- cpkTrendChartInstance = echarts.init(chartDom);
- // 获取数据
- const dates = [];
- const cpkValues = [];
- // 按时间排序数据
- const sortedData = [...capList.value].sort((a, b) => {
- return new Date(a.dataTime) - new Date(b.dataTime);
- });
- // 提取CPK值与对应日期
- sortedData.forEach(item => {
- if (item.cpkValue !== null && item.cpkValue !== undefined) {
- dates.push(parseTime(item.dataTime, '{y}-{m}-{d}'));
- cpkValues.push(parseFloat(item.cpkValue));
- }
- });
- const option = {
- title: {
- text: '整体生产过程能力趋势',
- subtext: '评价cpk值,高值为优',
- left: 'center'
- },
- tooltip: {
- trigger: 'axis'
- },
- xAxis: {
- type: 'category',
- data: dates
- },
- yAxis: {
- type: 'value',
- name: 'CPK值'
- },
- series: [{
- data: cpkValues,
- type: 'line',
- smooth: true,
- markLine: {
- data: [
- {
- yAxis: 1.33,
- lineStyle: {
- color: '#52c41a'
- },
- label: {
- formatter: '目标值: 1.33',
- position: 'middle'
- }
- }
- ],
- symbol: ['none', 'none']
- }
- }],
- grid: {
- left: '3%',
- right: '4%',
- bottom: '10%',
- top: '15%',
- containLabel: true
- }
- };
- cpkTrendChartInstance.setOption(option);
- }
- // 绘制标准差趋势图
- function drawStdDevTrendChart() {
- const chartDom = stdDevTrendChartRef.value;
- if (!chartDom) return;
- if (stdDevTrendChartInstance) {
- stdDevTrendChartInstance.dispose();
- }
- stdDevTrendChartInstance = echarts.init(chartDom);
- // 获取数据
- const dates = [];
- const stdDevValues = [];
- // 按时间排序数据
- const sortedData = [...capList.value].sort((a, b) => {
- return new Date(a.dataTime) - new Date(b.dataTime);
- });
- // 提取标准差值与对应日期
- sortedData.forEach(item => {
- if (item.stdDeviation !== null && item.stdDeviation !== undefined) {
- dates.push(parseTime(item.dataTime, '{y}-{m}-{d}'));
- stdDevValues.push(parseFloat(item.stdDeviation));
- }
- });
- const option = {
- title: {
- text: '工艺稳定性趋势',
- subtext: '评价标准差值,低值为优',
- left: 'center'
- },
- tooltip: {
- trigger: 'axis'
- },
- xAxis: {
- type: 'category',
- data: dates
- },
- yAxis: {
- type: 'value',
- name: '标准偏差'
- },
- series: [{
- data: stdDevValues,
- type: 'line',
- smooth: true
- }],
- grid: {
- left: '3%',
- right: '4%',
- bottom: '10%',
- top: '15%',
- containLabel: true
- }
- };
- stdDevTrendChartInstance.setOption(option);
- }
- // 计算预测结果
- function calculatePrediction() {
- if (capList.value.length === 0) {
- predictionResult.value = {
- cpkPrediction: 0,
- stdDevPrediction: 0,
- trend: '无数据',
- recommendation: '暂无数据可供预测'
- };
- return;
- }
- // 按时间排序数据
- const sortedData = [...capList.value].sort((a, b) => {
- return new Date(a.dataTime) - new Date(b.dataTime);
- });
- // 获取最近的CPK和标准差值
- const latestItem = sortedData[sortedData.length - 1];
- const currentCpk = parseFloat(latestItem.cpkValue) || 1.33;
- const currentStdDev = parseFloat(latestItem.stdDeviation) || 10;
- // 简单预测:基于最近的趋势(使用最后5个数据点)
- let cpkTrend = 0;
- let stdDevTrend = 0;
- if (sortedData.length >= 5) {
- // 计算CPK趋势
- const recentCpkValues = sortedData.slice(-5).map(item => parseFloat(item.cpkValue) || 0);
- let cpkSum = 0;
- for (let i = 1; i < recentCpkValues.length; i++) {
- cpkSum += (recentCpkValues[i] - recentCpkValues[i-1]);
- }
- cpkTrend = cpkSum / (recentCpkValues.length - 1);
- // 计算标准差趋势
- const recentStdDevValues = sortedData.slice(-5).map(item => parseFloat(item.stdDeviation) || 0);
- let stdDevSum = 0;
- for (let i = 1; i < recentStdDevValues.length; i++) {
- stdDevSum += (recentStdDevValues[i] - recentStdDevValues[i-1]);
- }
- stdDevTrend = stdDevSum / (recentStdDevValues.length - 1);
- }
- const predictedCpk = parseFloat((currentCpk + cpkTrend).toFixed(2));
- const predictedStdDev = parseFloat((currentStdDev + stdDevTrend).toFixed(2));
- // 确定趋势描述
- let trend = '稳定趋势';
- if (cpkTrend > 0.05) {
- trend = '上升趋势';
- } else if (cpkTrend < -0.05) {
- trend = '下降趋势';
- }
- // 生成建议
- let recommendation = '';
-
- // 根据CPK趋势判断整体过程能力描述
- let cpkTrendDesc = '';
- if (cpkTrend > 0.05) {
- cpkTrendDesc = '提升趋势明显';
- } else if (cpkTrend < -0.05) {
- cpkTrendDesc = '下降趋势明显';
- } else {
- cpkTrendDesc = '稳定趋势';
- }
-
- // 根据标准差趋势判断工艺稳定性描述
- let stdDevTrendDesc = '';
- if (stdDevTrend > 0.5) {
- stdDevTrendDesc = '下降趋势明显';
- } else if (stdDevTrend < -0.5) {
- stdDevTrendDesc = '提升趋势明显';
- } else {
- stdDevTrendDesc = '稳定趋势';
- }
-
- if (predictedCpk < 1.0) {
- recommendation = '整体过程能力:' + cpkTrendDesc + '<br/>工艺稳定性:' + stdDevTrendDesc + '<br/>整体过程控制能力:不足,请立即采取纠正措施';
- } else if (predictedCpk < 1.33) {
- recommendation = '整体过程能力:' + cpkTrendDesc + '<br/>工艺稳定性:' + stdDevTrendDesc + '<br/>整体过程控制能力:基本满足要求,建议加强监控';
- } else if (predictedCpk < 1.67) {
- recommendation = '整体过程能力:' + cpkTrendDesc + '<br/>工艺稳定性:' + stdDevTrendDesc + '<br/>整体过程控制能力:满足要求,保持当前状态';
- } else {
- recommendation = '整体过程能力:' + cpkTrendDesc + '<br/>工艺稳定性:' + stdDevTrendDesc + '<br/>整体过程控制能力:良好,请继续保持';
- }
- predictionResult.value = {
- cpkPrediction: predictedCpk,
- stdDevPrediction: predictedStdDev,
- trend: trend,
- recommendation: recommendation
- };
- }
- // 获取趋势标签类型
- function getTrendTagType(trend) {
- if (!trend || trend === '0' || trend === 0 || trend === '稳定趋势') return 'warning';
- if (trend.includes('上升')) return 'success';
- if (trend.includes('下降')) return 'danger';
- return 'info';
- }
- // 获取趋势颜色
- function getTrendColor(trend) {
- if (!trend || trend === '0' || trend === 0) return '#909399';
- if (trend.includes('上升')) return '#67C23A';
- if (trend.includes('下降')) return '#F56C6C';
- if (trend.includes('稳定')) return '#E6A23C';
- return '#909399';
- }
- // 获取趋势文本
- function getTrendText(trend) {
- if (!trend || trend === '0' || trend === 0) return '稳定趋势';
- return trend;
- }
- // 取消按钮
- 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}]
- // 转换为1行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]
- // 转换为1行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);
- }
- 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(() => {
- });
- }
- /** 显示标准差前5的记录 */
- function showTopStdDev() {
- // 按标准差降序排序,取前5条记录
- const sortedList = [...capList.value]
- .filter(item => item.stdDeviation !== null && item.stdDeviation !== undefined)
- .sort((a, b) => parseFloat(b.stdDeviation) - parseFloat(a.stdDeviation))
- .slice(0, 5);
-
- topStdDevList.value = sortedList;
- topStdDevOpen.value = true;
- }
- /** 导出按钮操作 */
- 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);
- }
- /** 测定值添加按钮操作(1行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 = '';
- }
- });
- }
- });
- // 监听窗口大小变化,自动调整图表大小
- const handleResize = () => {
- if (cpkTrendChartInstance) {
- cpkTrendChartInstance.resize();
- }
- if (stdDevTrendChartInstance) {
- stdDevTrendChartInstance.resize();
- }
- };
- onMounted(() => {
- getList();
- window.addEventListener('resize', handleResize);
- });
- onBeforeUnmount(() => {
- window.removeEventListener('resize', handleResize);
- // 清除图表实例
- if (cpkTrendChartInstance) {
- cpkTrendChartInstance.dispose();
- }
- if (stdDevTrendChartInstance) {
- stdDevTrendChartInstance.dispose();
- }
- });
- // 计算标准偏差等级评价数据
- 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
- }
- </script>
- <style>
- .cpk-current-row td {
- background-color: rgba(255, 215, 0, 0.3) !important; /* 金色背景 */
- font-weight: bold;
- }
- .trend-analysis {
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- }
- .trend-title {
- font-size: 12px;
- color: #999;
- margin-bottom: 5px;
- }
- .trend-content {
- font-size: 18px;
- font-weight: bold;
- }
- </style>
|