Преглед на файлове

仪器设备维修后验证记录功能优化

shenqilong преди 10 месеца
родител
ревизия
8ca084d807
променени са 1 файла, в които са добавени 476 реда и са изтрити 0 реда
  1. 476 0
      src/views/component/deviceValidation/index.vue

+ 476 - 0
src/views/component/deviceValidation/index.vue

@@ -0,0 +1,476 @@
+<template>
+    <div>
+        <div v-if="show" class="reagentChange">
+            <el-row type="flex">
+                <el-col class="button">
+                    <div class="title">仪器设备维修后验证记录(定量)</div>
+                    <div v-if="nodeId === 'Activity_0b188bo' || readonly" />
+                    <div v-else>
+                        <el-button type="primary" size="mini" icon="ibps-icon-add" @click="handleAdd"> 添加</el-button>
+                        <el-button type="danger" size="mini" icon="ibps-icon-remove" @click="handleDelete"> 删除</el-button>
+                        <el-button type="warning" size="mini" icon="ibps-icon-calculator" @click="computedResult">计算</el-button>
+
+                    </div>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col>
+                    <el-table ref="reagent" :data="reagentDataFilter" :span-method="spanMethod" @selection-change="handleSelectionChange">
+                        <el-table-column type="selection" width="55" />
+                        <el-table-column
+                            label="检验项目"
+                            prop="jianYanXiangMu"
+                            width="150"
+                        >
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.jianYanXiangMu" type="textarea" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.jianYanXiangMu|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="浓度" prop="nongDu" width="100">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.nongDu" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.nongDu|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="条码号或标本号" prop="tiaoBiaoHao" width="150">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.tiaoBiaoHao" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.tiaoBiaoHao|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="故障前结果(X)" prop="qianJieGuo" width="130">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.qianJieGuo" :min="0" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.qianJieGuo|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="故障后结果(Y)" prop="houJieGuo" width="130">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.houJieGuo" :min="0" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.houJieGuo|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="计算方式" prop="jiSuanGongShi" width="140">
+                            <template slot-scope="{row}">
+                                <el-select v-if="!disabled" v-model="row.jiSuanGongShi" size="mini" placeholder="请选择">
+                                    <el-option
+                                        label="|Y-X|"
+                                        value="|Y-X|"
+                                    />
+                                    <el-option
+                                        label="|Y-X|/X(%)"
+                                        value="|Y-X|/X(%)"
+                                    />
+                                </el-select>
+                                <span v-else>{{ row.jiSuanGongShi|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="实际差值" prop="shiJiChaZhi" />
+                        <el-table-column label="实际偏倚" prop="shiJiPianYi" />
+                        <el-table-column label="限定范围" prop="xianDingFanWei" width="110">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.xianDingFanWei" size="mini" placeholder="请输入" type="number" />
+                                <span v-else>{{ row.xianDingFanWei|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="允许偏倚" prop="yunXuPianYi" width="100">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.yunXuPianYi" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.yunXuPianYi|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="是否相符" prop="shiFouXiangFu" width="80" />
+                        <el-table-column label="符合率" prop="fuHeLv" />
+                        <el-table-column label="符合率可接受标准" prop="fuHeBiaoZhun" width="140">
+                            <template slot-scope="{row}">
+                                <el-select v-if="!disabled" v-model="row.fuHeBiaoZhun" size="mini" placeholder="请选择" @change="handelChange($event,row.jianYanXiangMu)">
+                                    <el-option
+                                        label="≥80%"
+                                        value="≥80%"
+                                    />
+                                    <el-option
+                                        label="=100%"
+                                        value="=100%"
+                                    />
+                                </el-select>
+                                <span v-else>{{ row.fuHeBiaoZhun|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="结论" prop="jieLun" />
+                    </el-table>
+                    <el-pagination
+                        layout="total,sizes,prev, pager, next,jumper"
+                        :current-page="requestPage.pageNo"
+                        :page-size="requestPage.limit"
+                        :page-sizes="[10,15,20,30,50,100]"
+                        :total="reagentData.length"
+                        @size-change="handleSizeChange"
+                        @current-change="handleCurrentChange"
+                    />
+                </el-col>
+            </el-row>
+        </div>
+
+    </div>
+</template>
+<script>
+export default {
+    props: {
+        formData: {
+            type: Object,
+            default: () => {}
+        },
+        readonly: {
+            type: Boolean,
+            default: false
+        },
+        params: {
+            type: Object,
+            default: () => {}
+        }
+    },
+    data () {
+        return {
+            reagentData: [],
+            copyDialogData: [],
+            disabled: false,
+            ypData: [],
+            nodeId: '',
+            show: false,
+            requestPage: {
+                limit: 20,
+                pageNo: 1
+            },
+            importTableDialogVisible: false,
+            multipleSelection: []
+
+        }
+    },
+    computed: {
+        reagentDataFilter () {
+            return this.reagentData.slice((this.requestPage.pageNo - 1) * (this.requestPage.limit), (this.requestPage.pageNo - 1) * (this.requestPage.limit) + this.requestPage.limit)
+        }
+    },
+    watch: {
+        'formData.yqsbwxhlybdjlbzbdl': {
+            handler (value, old) {
+                if (value && value.length) {
+                    this.reagentData = value
+                }
+            },
+            immediate: true
+        },
+        reagentData: {
+            handler (value, old) {
+                this.$emit('change-data', 'yqsbwxhlybdjlbzbdl', value)
+            },
+            deep: true
+        },
+        'formData.xiangMuLeiXing': {
+            handler (val) {
+                if (val === '定量') { this.show = true } else { this.show = false; this.reagentData = [] }
+            },
+            immediate: true
+        },
+        'formData.piLiangYunXu': {
+            handler (val) {
+                this.reagentData.forEach(item => {
+                    const { jianYanXiangMu } = this.formData
+                    if (item.jianYanXiangMu === jianYanXiangMu) {
+                        item.yunXuPianYi = val
+                    }
+                })
+            },
+            immediate: true
+        },
+        'formData.piLiangXianDing': {
+            handler (val) {
+                this.reagentData.forEach(item => {
+                    const { jianYanXiangMu } = this.formData
+                    if (item.jianYanXiangMu === jianYanXiangMu) {
+                        item.xianDingFanWei = val
+                    }
+                })
+            }
+        }
+    },
+    mounted () {
+        const { first = '' } = this.$store.getters.level
+        const { deptList = [] } = this.$store.getters || {}
+        const t1 = deptList.find(i => i.positionId === first) || {}
+
+        console.log(t1.positionName)
+        this.nodeId = this.params ? this.params.nodeId : ''
+        this.disabled = this.readonly || this.nodeId === 'Activity_0b188bo'
+    },
+    methods: {
+        handelChange (event, jianYanXiangMu) {
+            this.reagentData.forEach(item => {
+                if (item.jianYanXiangMu === jianYanXiangMu) {
+                    item.fuHeBiaoZhun = event
+                }
+            })
+        },
+        // 新增
+        handleAdd () {
+            const { xMMingCheng, piLiangYunXu, piLiangXianDing, piLiangNongDu } = this.formData
+            if (this.disabled && this.reagentData?.length > 0) {
+                return this.$message.warning('导入与手动添加功能不可混用!')
+            }
+            if (!this.formData.xMMingCheng) {
+                return this.$message.warning('请选择或输入检验项目')
+            }
+            this.reagentData.push({ jianYanXiangMu: xMMingCheng, nongDu: piLiangNongDu, tiaoBiaoHao: '', qianJieGuo: '', houJieGuo: '', jiSuanGongShi: '|Y-X|/X(%)', shiJiChaZhi: '', shiJiPianYi: '', xianDingFanWei: piLiangXianDing, yunXuPianYi: piLiangYunXu, shiFouXiangFu: '', fuHeLv: '', fuHeBiaoZhun: '≥80%', jieLun: '' })
+            const grouped = this.reagentData.reduce((acc, item) => {
+                const key = item.jianYanXiangMu
+                if (!acc[key]) acc[key] = []
+                acc[key].push(item)
+                return acc
+            }, {})
+            // 先清空数组,再填充新数据(确保 Vue 检测到变化)
+            this.reagentData.splice(0, this.reagentData.length, ...Object.values(grouped).flat())
+            // console.log(this.reagentData)
+        },
+
+        // 删除
+        handleDelete () {
+            this.$confirm('确定删除当前选中数据?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                if (this.multipleSelection.length > 0) {
+                    this.reagentData = this.reagentData.filter(row => !this.multipleSelection.includes(row))
+                } else {
+                    this.$message.warning('请选择数据')
+                }
+            })
+        },
+        handleSelectionChange (val) {
+            this.multipleSelection = val
+        },
+
+        getColumns () {
+            return [{
+                field_name: 'shiFouXiangFu',
+                label: '是否相符',
+                name: 'shiFouXiangFu'
+            },
+            {
+                field_name: 'jianYanXiangMu',
+                label: '检验项目',
+                name: 'jianYanXiangMu'
+            },
+            {
+                field_name: 'nongDu',
+                label: '浓度',
+                name: 'nongDu'
+            },
+            {
+                field_name: 'tiaoBiaoHao',
+                label: '条码号或标本号',
+                name: 'tiaoBiaoHao'
+            },
+            {
+                field_name: 'qianJieGuo',
+                label: '故障前结果(X)',
+                name: 'qianJieGuo'
+            },
+            {
+                field_name: 'houJieGuo',
+                label: '故障后结果(Y)',
+                name: 'houJieGuo'
+            },
+            {
+                field_name: 'shiJiChaZhi',
+                label: '实际差值',
+                name: 'shiJiChaZhi'
+            },
+            {
+                field_name: 'shiJiPianYi',
+                label: '实际偏倚',
+                name: 'shiJiPianYi'
+            },
+            {
+                field_name: 'xianDingFanWei',
+                label: '限定范围',
+                name: 'xianDingFanWei'
+            },
+            {
+                field_name: 'yunXuPianYi',
+                label: '允许偏倚',
+                name: 'yunXuPianYi'
+            },
+            {
+                field_name: 'fuHeLv',
+                label: '符合率',
+                name: 'fuHeLv'
+            }, {
+                field_name: 'jieLun',
+                label: '结论',
+                name: 'jieLun'
+            },
+            {
+                field_name: 'fuHeBiaoZhun',
+                label: '符合率可接受标准',
+                name: 'fuHeBiaoZhun'
+            }]
+        },
+
+        // 当前页码改变
+        handleCurrentChange (val) {
+            this.requestPage.pageNo = val
+        },
+        // 页码选择器改变
+        handleSizeChange (val) {
+            this.requestPage.limit = val
+            this.requestPage.pageNo = 1
+        },
+
+        // 计算结果
+        computedResult () {
+            if (this.disabled) {
+                this.$message.warning('导入数据请勿使用计算功能!')
+                return
+            }
+            const hasInvalidData = this.reagentData.some(item =>
+                !item.qianJieGuo || item.qianJieGuo <= 0 || !item.houJieGuo || item.houJieGuo <= 0
+            )
+
+            if (hasInvalidData || this.reagentData.length === 0) {
+                this.$message.warning('故障前结果(X)必须大于0且不能为空!')
+                return
+            }
+
+            const hasPyOrCz = this.reagentData.some(item =>
+                (item.jiSuanGongShi === '|Y-X|/X(%)' && !item.yunXuPianYi) || (item.jiSuanGongShi === '|Y-X|' && !item.xianDingFanWei)
+            )
+
+            if (hasPyOrCz) {
+                this.$message.warning(`计算方式为|Y-X|/X(%)请填写允许偏倚,计算方式为|Y-X|请填写限定范围`)
+                return
+            }
+            // 预处理函数:统一处理百分比数值
+            const normalizePercent = val => Number(String(val).replace('%', '')) || 0
+
+            // 使用 Map 存储统计信息
+            const projectStats = new Map()
+
+            this.reagentData.forEach(item => {
+                const { jiSuanGongShi, houJieGuo, qianJieGuo, xianDingFanWei, jianYanXiangMu } = item
+
+                // 计算差值/偏倚
+                if (!isNaN(houJieGuo) && !isNaN(qianJieGuo)) {
+                    if (jiSuanGongShi === '|Y-X|') {
+                        item.shiJiChaZhi = Math.abs((houJieGuo * 100000 - qianJieGuo * 100000) / 100000)
+                        item.shiFouXiangFu = normalizePercent(item.shiJiChaZhi) <= normalizePercent(xianDingFanWei) ? '相符' : '不相符'
+                        item.shiJiPianYi = ''
+                    } else {
+                        item.shiJiPianYi = this.deleteAccuracy(Math.abs(houJieGuo - qianJieGuo) / qianJieGuo)
+                        item.shiFouXiangFu = normalizePercent(item.shiJiPianYi) <= normalizePercent(item.yunXuPianYi) ? '相符' : '不相符'
+                        item.shiJiChaZhi = ''
+                    }
+                }
+
+                // 统计符合率
+                const stats = projectStats.get(jianYanXiangMu) || { total: 0, yes: 0 }
+                stats.total++
+                if (item.shiFouXiangFu === '相符') stats.yes++
+                projectStats.set(jianYanXiangMu, stats)
+            })
+
+            // 填充符合率标准,保留小数点后两位
+            this.reagentData.forEach(item => {
+                const stats = projectStats.get(item.jianYanXiangMu)
+                item.fuHeLv = this.deleteAccuracy(stats.yes / stats.total)
+                console.log(parseFloat(item.fuHeLv), item.fuHeBiaoZhun)
+                item.jieLun = parseFloat(item.fuHeLv) >= parseFloat(item.fuHeBiaoZhun.replace(/[≥=%]/g, '')) ? '可接受' : '不可接受'
+            })
+        },
+        spanMethod ({ row, column, rowIndex, columnIndex }) {
+            if (columnIndex === 1 || columnIndex === 12 || columnIndex === 13 || columnIndex === 14) {
+                const currentValue = row[column.property]
+                const preRow = this.reagentData[rowIndex - 1]
+                // 上一行这一列的数据
+                const preValue = preRow ? preRow[column.property] : null
+                // 如果当前值和上一行的值相同,则将当前单元格隐藏
+                // 给第0,10,11,12列对数值相同且是同一个'jianYanXiangMu'进行表格合并
+                if (currentValue === preValue && row['jianYanXiangMu'] === preRow['jianYanXiangMu']) {
+                    return { rowspan: 0, colspan: 0 }
+                } else {
+                    let rowspan = 1
+                    // 计算应该合并的行数
+                    for (let i = rowIndex + 1; i < this.reagentData.length; i++) {
+                        const nextRow = this.reagentData[i]
+                        const nextValue = nextRow[column.property]
+                        if (nextValue === currentValue && nextRow['jianYanXiangMu'] === row['jianYanXiangMu']) {
+                            rowspan++
+                        } else {
+                            break
+                        }
+                    }
+                    return { rowspan, colspan: 1 }
+                }
+                // if (rowIndex % rowspan === 0) {
+                //     return {
+                //         rowspan,
+                //         colspan: 1
+                //     }
+                // } else {
+                //     return {
+                //         rowspan: 0,
+                //         colspan: 0
+                //     }
+                // }
+            }
+        },
+        // 去除小数*100精度方法
+        deleteAccuracy (num) {
+            // 是否带小数点
+            if ((num.toString()).includes('.')) {
+                // 保留小数点后面3位
+                const numArry = num.toFixed(3).toString().split('.')
+                // 整数位是否大于0
+                if (numArry[0] > 0) {
+                    return Number(numArry[0] + numArry[1].substring(0, 2) + '.' + numArry[1].substring(2, 3)) + '%'
+                } else {
+                    // 小数位第一位是否大于0
+                    if (numArry[1][0] > 0) {
+                        return Number(numArry[1].substring(0, 2) + '.' + numArry[1].substring(2, 3)) + '%'
+                    } else {
+                        // 小数位第二位是否大于0
+                        if (numArry[1][1] > 0) {
+                            return Number(numArry[1].substring(1, 2) + '.' + numArry[1].substring(2, 3)) + '%'
+                        } else {
+                            return Number(0 + '.' + numArry[1].substring(2, 3)) + '%'
+                        }
+                    }
+                }
+            } else {
+                return num * 100 + '%'
+            }
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+.reagentChange{
+margin-bottom: 20px;
+.button{
+    display: flex;
+    justify-content: space-between;
+    padding: 0px 0px 0px 15px;
+    background: #f0ffff;
+    .title {
+        color: #999;
+        font-size: 12px;
+        font-weight: bold;
+        margin-bottom: 0;
+    }
+    .el-button {
+        margin: 0;
+    }
+}
+}
+</style>