Selaa lähdekoodia

同步实际更换验证定量到通用版

wangxiaoyi 9 kuukautta sitten
vanhempi
sitoutus
ab862c27dc
1 muutettua tiedostoa jossa 551 lisäystä ja 0 poistoa
  1. 551 0
      src/views/component/newReagent/newReagent.vue

+ 551 - 0
src/views/component/newReagent/newReagent.vue

@@ -0,0 +1,551 @@
+<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_0xkc1ji' || readonly" />
+                    <div v-else>
+                        <span style="color: red;margin-right: 20px;font-size: 16px;">注意:手动计算与导入不可混用</span>
+                        <el-button type="primary" size="mini" icon="ibps-icon-add" @click="handleAdd"> 添加</el-button>
+                        <el-button type="primary" size="mini" icon="ibps-icon-copy" @click="handleCopy"> 复制</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>
+                        <el-button type="primary" size="mini" icon="ibps-icon-download" @click="handleDownload">模版下载</el-button>
+                        <el-button type="primary" size="mini" icon="ibps-icon-import" @click="handleImport">导入</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="jianCeXiangMu"
+                        />
+                        <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="biaoBenHao" width="100">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.biaoBenHao" size="mini" placeholder="请输入" />
+                                <span v-else>{{ row.biaoBenHao|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="旧试剂测得结果" prop="jiuJieGuo" width="130">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.jiuJieGuo" :min="0" size="mini" placeholder="请输入" type="number" />
+                                <span v-else>{{ row.jiuJieGuo|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="新试剂测得结果" prop="xinJieGuo" width="130">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.xinJieGuo" :min="0" size="mini" placeholder="请输入" type="number" />
+                                <span v-else>{{ row.xinJieGuo|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="计算方式" prop="jiSuanFangShi" width="140">
+                            <template slot-scope="{row}">
+                                <el-select v-if="!disabled" v-model="row.jiSuanFangShi" 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.jiSuanFangShi|| '/' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="实际差值" prop="shiJiChaZhi" />
+                        <el-table-column label="实际偏倚" prop="jieGuo" />
+                        <el-table-column :label="isHz?'允许差值':'限定范围'" prop="zuiXiaoFanWei" width="110">
+                            <template slot-scope="{row}">
+                                <el-input v-if="!disabled" v-model="row.zuiXiaoFanWei" size="mini" placeholder="请输入" type="number" />
+                                <span v-else>{{ row.zuiXiaoFanWei|| '/' }}</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="xiangFu" width="80" />
+                        <el-table-column label="符合率" prop="biaoZhun" />
+                        <el-table-column label="符合率可接受标准" prop="xiangMuFuHeLv" width="140">
+                            <template slot-scope="{row}">
+                                <el-select v-if="!disabled" v-model="row.xiangMuFuHeLv" size="mini" placeholder="请选择" @change="handelChange($event,row.jianCeXiangMu)">
+                                    <el-option
+                                        label="≥80%"
+                                        value="≥80%"
+                                    />
+                                    <el-option
+                                        label="=100%"
+                                        value="=100%"
+                                    />
+                                </el-select>
+                                <span v-else>{{ row.xiangMuFuHeLv|| '/' }}</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>
+        <import-table
+            :visible="importTableDialogVisible"
+            title="导入"
+            @close="(visible) => (importTableDialogVisible = visible)"
+            @action-event="handleImportTableActionEvent"
+        />
+    </div>
+</template>
+<script>
+import importTable from '@/business/platform/form/formrender/dynamic-form/components/import-table'
+import IbpsImport from '@/plugins/import'
+import { downloadFile } from '@/business/platform/file/utils'
+import { cloneDeep } from 'lodash'
+export default {
+    components: {
+        importTable
+    },
+    props: {
+        formData: {
+            type: Object,
+            default: () => {}
+        },
+        readonly: {
+            type: Boolean,
+            default: false
+        },
+        params: {
+            type: Object,
+            default: () => {}
+        }
+    },
+    data () {
+        return {
+            reagentData: [],
+            copyDialogData: [],
+            disabled: false,
+            ypData: [],
+            nodeId: '',
+            show: true,
+            requestPage: {
+                limit: 20,
+                pageNo: 1
+            },
+            importTableDialogVisible: false,
+            multipleSelection: [],
+            isHz: false
+        }
+    },
+    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.sjghyzjlbbbzb': {
+            handler (value, old) {
+                if (value && value.length) {
+                    this.reagentData = value
+                }
+            },
+            immediate: true
+        },
+        reagentData: {
+            handler (value, old) {
+                this.$emit('change-data', 'sjghyzjlbbbzb', value)
+            },
+            deep: true
+        },
+        'formData.fangAn': {
+            handler (val) {
+                this.showAndHide(val)
+            },
+            immediate: true
+        },
+        'formData.yunXuPianYi': {
+            handler (val) {
+                this.reagentData.forEach(item => {
+                    const { jianYanXiangMu } = this.formData
+                    if (item.jianCeXiangMu === jianYanXiangMu) {
+                        item.yunXuPianYi = val
+                    }
+                })
+            },
+            immediate: true
+        },
+        'formData.xianDingFanWei': {
+            handler (val) {
+                this.reagentData.forEach(item => {
+                    const { jianYanXiangMu } = this.formData
+                    if (item.jianCeXiangMu === jianYanXiangMu) {
+                        item.zuiXiaoFanWei = val
+                    }
+                })
+            }
+        }
+    },
+    mounted () {
+        const { first = '' } = this.$store.getters.level
+        const { deptList = [] } = this.$store.getters || {}
+        const t1 = deptList.find(i => i.positionId === first) || {}
+        this.isHz = t1.positionName === '惠州市第三人民医院'
+        console.log(t1.positionName)
+        this.nodeId = this.params ? this.params.nodeId : ''
+        this.disabled = this.readonly || this.nodeId === 'Activity_0xkc1ji'
+        this.showAndHide(this.formData.fangAn)
+    },
+    methods: {
+        handelChange (event, jianCeXiangMu) {
+            this.reagentData.forEach(item => {
+                if (item.jianCeXiangMu == jianCeXiangMu) {
+                    item.xiangMuFuHeLv = event
+                }
+            })
+        },
+        // 新增
+        handleAdd () {
+            const { jianYanXiangMu, yunXuPianYi, xianDingFanWei } = this.formData
+            if (this.disabled && this.reagentData?.length > 0) {
+                return this.$message.warning('导入与手动添加功能不可混用!')
+            }
+            if (!this.formData.jianYanXiangMu) {
+                return this.$message.warning('请选择检验项目')
+            }
+            this.reagentData.push({ jianCeXiangMu: jianYanXiangMu, nongDu: '', biaoBenHao: '', jiuJieGuo: '', xinJieGuo: '', jiSuanFangShi: '|Y-X|/X(%)', shiJiChaZhi: '', jieGuo: '', zuiXiaoFanWei: xianDingFanWei, yunXuPianYi: yunXuPianYi, xiangFu: '', biaoZhun: '', xiangMuFuHeLv: '≥80%', jieLun: '' })
+            const grouped = this.reagentData.reduce((acc, item) => {
+                const key = item.jianCeXiangMu
+                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)
+        },
+        // 复制
+        handleCopy () {
+            if (this.multipleSelection.length > 0) {
+                this.reagentData = this.reagentData.concat(cloneDeep(this.multipleSelection))
+                const grouped = this.reagentData.reduce((acc, item) => {
+                    const key = item.jianCeXiangMu
+                    if (!acc[key]) acc[key] = []
+                    acc[key].push(item)
+                    return acc
+                }, {})
+                // 先清空数组,再填充新数据(确保 Vue 检测到变化)
+                this.reagentData.splice(0, this.reagentData.length, ...Object.values(grouped).flat())
+            } else {
+                this.$message.warning('请选择数据')
+            }
+        },
+        // 删除
+        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
+        },
+        handleImport () {
+            this.importTableDialogVisible = true
+        },
+        handleDownload () {
+            downloadFile({ id: 'download_sjghdl', fileName: '试剂更换验证定量模板', ext: 'xlsx' })
+        },
+        getColumns () {
+            return [{
+                field_name: 'xiangFu',
+                label: '是否相符',
+                name: 'xiangFu'
+            },
+            {
+                field_name: 'jianCeXiangMu',
+                label: '检验项目',
+                name: 'jianCeXiangMu'
+            },
+            {
+                field_name: 'nongDu',
+                label: '浓度',
+                name: 'nongDu'
+            },
+            {
+                field_name: 'biaoBenHao',
+                label: '样品编号',
+                name: 'biaoBenHao'
+            },
+            {
+                field_name: 'jiuJieGuo',
+                label: '旧试剂测得结果',
+                name: 'jiuJieGuo'
+            },
+            {
+                field_name: 'xinJieGuo',
+                label: '新试剂测得结果',
+                name: 'xinJieGuo'
+            },
+            {
+                field_name: 'shiJiChaZhi',
+                label: '实际差值',
+                name: 'shiJiChaZhi'
+            },
+            {
+                field_name: 'jieGuo',
+                label: '实际偏倚',
+                name: 'jieGuo'
+            },
+            {
+                field_name: 'zuiXiaoFanWei',
+                label: this.isHz ? '允许差值' : '限定范围',
+                name: 'zuiXiaoFanWei'
+            },
+            {
+                field_name: 'yunXuPianYi',
+                label: '允许偏倚',
+                name: 'yunXuPianYi'
+            },
+            {
+                field_name: 'biaoZhun',
+                label: '符合率',
+                name: 'biaoZhun'
+            }, {
+                field_name: 'jieLun',
+                label: '结论',
+                name: 'jieLun'
+            },
+            {
+                field_name: 'xiangMuFuHeLv',
+                label: '符合率可接受标准',
+                name: 'xiangMuFuHeLv'
+            }]
+        },
+        getKeys (data) {
+            return Array.isArray(data) ? data.reduce((acc, item) => ({ ...acc, [item.label]: item.name }), {}) : {}
+        },
+        handleImportTableActionEvent (file, options) {
+            IbpsImport.xlsx(file, options).then(({ header, results }) => {
+                const list = []
+                const keys = this.getKeys(this.getColumns())
+                results.forEach(item => {
+                    const obj = {}
+                    Object.keys(item).forEach(key => {
+                        if (keys[key]) {
+                            obj[keys[key]] = item[key]
+                        }
+                    })
+                    list.push(obj)
+                })
+                const filteredArray = list.map(item => {
+                    if (item.jianCeXiangMu && item.biaoZhun && item.jieLun) {
+                        return item
+                    }
+                    return null
+                }).filter(item => item !== null)
+                filteredArray.forEach(item => {
+                    list.forEach(el => {
+                        if (el.jianCeXiangMu === item.jianCeXiangMu) {
+                            el.biaoZhun = item.biaoZhun
+                            el.jieLun = item.jieLun
+                            el.xiangMuFuHeLv = item.xiangMuFuHeLv
+                        }
+                        if (el.shiJiChaZhi) {
+                            el.shiJiChaZhi = (el.shiJiChaZhi + '').replace('-', '')
+                        }
+                        if (el.jieGuo) {
+                            el.jieGuo = (el.jieGuo + '').replace('-', '')
+                        }
+                    })
+                })
+                this.reagentData = list
+                this.disabled = true
+                this.importTableDialogVisible = false
+            })
+        },
+        // 当前页码改变
+        handleCurrentChange (val) {
+            this.requestPage.pageNo = val
+        },
+        // 页码选择器改变
+        handleSizeChange (val) {
+            this.requestPage.limit = val
+            this.requestPage.pageNo = 1
+        },
+        showAndHide (data) {
+            if (data.includes('平行试验') || data.includes('留样再测') || data.includes('比对方案')) {
+                this.show = true
+            } else {
+                this.show = false
+            }
+        },
+        // 计算结果
+        computedResult () {
+            if (this.disabled) {
+                this.$message.warning('导入数据请勿使用计算功能!')
+                return
+            }
+            const hasInvalidData = this.reagentData.some(item =>
+                !item.jiuJieGuo || item.jiuJieGuo <= 0 || !item.xinJieGuo || item.xinJieGuo <= 0
+            )
+
+            if (hasInvalidData || this.reagentData.length === 0) {
+                this.$message.warning('新、旧试剂测得结果必须大于0且不能为空!')
+                return
+            }
+
+            const hasPyOrCz = this.reagentData.some(item =>
+                (item.jiSuanFangShi === '|Y-X|/X(%)' && !item.yunXuPianYi) || (item.jiSuanFangShi === '|Y-X|' && !item.zuiXiaoFanWei)
+            )
+
+            if (hasPyOrCz) {
+                this.$message.warning(`计算方式为|Y-X|/X(%)请填写允许偏倚,计算方式为|Y-X|请填写${this.isHz ? '允许差值' : '限定范围'}`)
+                return
+            }
+            // 预处理函数:统一处理百分比数值
+            const normalizePercent = val => Number(String(val).replace('%', '')) || 0
+
+            // 使用 Map 存储统计信息
+            const projectStats = new Map()
+
+            this.reagentData.forEach(item => {
+                const { jiSuanFangShi, xinJieGuo, jiuJieGuo, zuiXiaoFanWei, jianCeXiangMu } = item
+
+                // 计算差值/偏倚
+                if (jiSuanFangShi === '|Y-X|') {
+                    item.shiJiChaZhi = Math.abs((xinJieGuo * 100000 - jiuJieGuo * 100000) / 100000)
+                    item.xiangFu = normalizePercent(item.shiJiChaZhi) <= normalizePercent(zuiXiaoFanWei) ? '相符' : '不相符'
+                    item.jieGuo = ''
+                } else {
+                    item.jieGuo = this.deleteAccuracy(Math.abs(xinJieGuo - jiuJieGuo) / jiuJieGuo)
+                    item.xiangFu = normalizePercent(item.jieGuo) <= normalizePercent(item.yunXuPianYi) ? '相符' : '不相符'
+                    item.shiJiChaZhi = ''
+                }
+
+                // 统计符合率
+                const stats = projectStats.get(jianCeXiangMu) || { total: 0, yes: 0 }
+                stats.total++
+                if (item.xiangFu === '相符') stats.yes++
+                projectStats.set(jianCeXiangMu, stats)
+            })
+
+            // 填充符合率标准,保留小数点后两位
+            this.reagentData.forEach(item => {
+                const stats = projectStats.get(item.jianCeXiangMu)
+                item.biaoZhun = this.deleteAccuracy(stats.yes / stats.total)
+                console.log(parseFloat(item.biaoZhun), item.xiangMuFuHeLv)
+                item.jieLun = parseFloat(item.biaoZhun) >= parseFloat(item.xiangMuFuHeLv.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列对数值相同且是同一个'jianCeXiangMu'进行表格合并
+                if (currentValue === preValue && row['jianCeXiangMu'] === preRow['jianCeXiangMu']) {
+                    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['jianCeXiangMu'] === row['jianCeXiangMu']) {
+                            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>