Files
zgty-mas-m/pages/analysis/sample/sample-work-detail.vue
2025-10-10 18:16:14 +08:00

1343 lines
36 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view>
<navbar-back :autoBack="false" titleWidth="800" :title="title" @leftClick="customBack">
<view class="navbar-right" @click="navRightClick">
<text>填写指派单</text>
<u-icon color="" name="edit-pen" size="19"></u-icon>
</view>
</navbar-back>
<u-row gutter="8">
<u-col span="3">
<view class="content-title-name">
<text>待化验样品</text>
</view>
<u-gap height="5" bg-color="#0055A2"></u-gap>
</u-col>
<u-col span="6">
<view class="content-title-name">
<view class="current-sample-code">{{ currentSample.sampleCode }}</view>
<view>数据采集或录入</view>
</view>
<u-gap height="5" bg-color="#0055A2"></u-gap>
</u-col>
<u-col span="3">
<view class="content-title-name">
<text>样品详情</text>
</view>
<u-gap height="5" bg-color="#0055A2"></u-gap>
</u-col>
</u-row>
<u-row gutter="8" align="top">
<u-col span="3">
<u-dropdown>
<u-dropdown-item
v-model="curParameterClassify"
:title="curParameterTitle"
height="340px"
:options="optionParameterClassify"
@change="parameterClassifyChange"
></u-dropdown-item>
</u-dropdown>
<scroll-view class="content-left-scroll" scroll-with-animation scroll-y :scroll-top="scrollTop">
<view
v-for="(sample, index) in leftList"
:key="index"
class="u-tab-item"
:class="[current.value === index ? 'u-tab-item-active' : '']"
:data-current="index"
@tap.stop="switchSample(index, false)"
>
<view class="pr5"> {{ sample.sort }} </view>
<view>
<view>
{{ sample.sampleCode }}
</view>
<view> {{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }} </view>
</view>
</view>
</scroll-view>
<u-button class="btn-operation" type="primary" @click="submitTask()">提交指派单</u-button>
</u-col>
<u-col span="6">
<view class="field-name" v-html="selectedField.value.name" />
<zzjc-num-keyboard
ref="myKeyboard"
v-show="selectedField.value.fillingWay === '1' && selectedField.value.type !== 'select'"
:numKeyboardParam="numKeyboardParam"
></zzjc-num-keyboard>
<view v-if="selectedField.value.fillingWay === '2'" class="y-f">
<view class="auncel" @click="selectAuncel">
<view class="auncel-title"> {{ currentAuncel.code }}</view>
<view class="auncel-weight">
<view class="weight">
<view
:style="{ textAlign: currentAuncel.isConnected != 1 ? 'center' : 'right' }"
:class="
currentAuncel.weightStable === 0
? 'weight-data-yellow'
: currentAuncel.weightStable === 1
? 'weight-data'
: 'weight-data-warning'
"
>
{{ currentAuncel.weightData }}
</view>
</view>
<view class="weight-unit">{{ currentAuncel.weightUnit }}</view>
</view>
</view>
<u-button
class="btn-operation"
v-if="currentAuncel.code !== ''"
type="success"
:disabled="confirmWeightDisabled"
shape="circle"
@click="saveAuncelData"
>
确认采集
</u-button>
</view>
</u-col>
<u-col span="3">
<view>
<scroll-view class="content-right-scroll" scroll-y scroll-with-animation>
<view>
<u-form :model="curSample" ref="uForm" label-width="140">
<template v-for="(fields, groupIndex) in fieldGroup" :key="'group_' + groupIndex">
<view v-if="curParameterKey.value === '' || curParameterKey.value === fields.title">
<!-- 组名 -->
<view class="my-collapse" @click="fields.open = !fields.open">
<text class="title">{{ fields.title }}</text>
<u-icon :name="fields.open ? 'arrow-up' : 'arrow-down'"></u-icon>
</view>
<view
class="content"
:id="'elId' + groupIndex"
:style="{ height: fields.open ? collaHeights[groupIndex] + 'px' : '0' }"
>
<view
class="form-item-my"
v-for="(field, fieldIndex) in fields.fields"
@click="fieldClick(field, groupIndex + '-' + fieldIndex)"
:key="groupIndex + '-' + fieldIndex"
v-show="field.hidden !== 1 && field.hidden !== '1'"
:class="{ 'selected-field': selectedFieldIndex.value === groupIndex + '-' + fieldIndex }"
>
<view
:class="[
'label-my',
{ 'label-high-light': field.highlight === 1 || field.highlight === '1' }
]"
v-html="
field.name +
(typeof field.unit !== 'undefined' && field.unit !== null && field.unit !== ''
? '(' + field.unit + ')'
: '')
"
></view>
<view class="content-my">
<!--
如果是select渲染2个组件1个input、1个picker.
field.valueText用于显示picker选中的文本
-->
<u-input
style="width: 120px"
v-if="field.fillingWay === 4"
v-model="field.value"
:placeholder="field.placeholder"
type="text"
/>
<view v-if="field.type === 'select'" class="x-bc select-my" @click="field.showPicker = true">
<text v-if="field.valueText">{{ field.valueText }}</text>
<text v-else>请选择</text>
<u-icon name="arrow-down" size="20"></u-icon>
</view>
<u-picker
v-if="field.type === 'select'"
:show="field.showPicker"
:columns="[field.options]"
keyName="dictValue"
@cancel="field.showPicker = false"
@confirm="event => dicPickerConfirm(event, field)"
/>
<!--普通输入框 使用文本显示-->
<view class="content-my-text" v-if="field.type !== 'select' && field.fillingWay !== 4">
<text v-if="!field.value || field.value === ''" class="content-my-text-placeholder">{{
field.placeholder
}}</text>
<text
v-else
:class="[
'content-my-text-value',
{ 'field-high-light': field.highlight === 1 || field.highlight === '1' }
]"
>{{ field.value }}</text
>
</view>
</view>
</view>
</view>
</view>
</template>
</u-form>
</view>
</scroll-view>
<u-button class="btn-operation" type="success" @click="saveDetail()">保存样品数据</u-button>
</view>
</u-col>
</u-row>
<!-- 天平选择-->
<auncel-select-popup
ref="auncelSelector"
v-model:showAuncelSelector="showAuncelSelector"
:previousAuncelId="currentAuncel.id"
@doSelect="auncelDoSelect"
></auncel-select-popup>
</view>
</template>
<script setup>
import { ref, reactive, computed, nextTick, onMounted } from 'vue'
import { onLoad, onBackPress, onShow, onHide, onUnload } from '@dcloudio/uni-app'
import request from '@/nx/request'
import { calcAnalysisValue } from '@/nx/helper/calcAnalysisValue'
import { number } from 'mathjs'
import AuncelSelectPopup from '@/components/sample/auncel-select-popup.vue'
import { getTenantId } from '@/defaultBaseUrl'
import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation'
import nx from '@/nx'
import text from '@/uview-plus/components/u-text/text'
// 响应式数据定义
const elId = nx.$helper.uuid()
const scrollTop = ref(0) //tab标题的滚动条位置
const current = ref(0) // 预设当前项的值
const menuHeight = ref(0) // 左边菜单的高度
const menuItemHeight = ref(0) // 左边菜单item的高度
const orderGenBH = ref(true) //顺序生成杯号
const weightDataIsToZero = ref(false) //重量数据是否归零
const currentSample = ref({}) //当前样品
const currentTaskNo = ref('') //当前任务指派单号
const currentTaskId = ref('') //当前任务指派单id
const currentTaskType = ref('') //当前任务类型
const title = ref('')
const numKeyboardParam = reactive({
decimal: '-1' //数字键盘小数位数,-1为不限制
})
const showAuncelSelector = ref(false) //显示天平选择框
const currentAuncel = reactive({
id: '',
name: '',
code: '',
weightStable: 0,
weightData: '请选天平',
weightUnit: ''
})
const selectedField = ref({})
const selectedFieldIndex = ref('')
const leftList = ref([])
const curSample = ref({})
const showDicPicker = ref(false)
const curParameterTitle = ref('选择字段分类')
const curParameterKey = ref('')
const curParameterClassify = ref('')
const conAssayTaskId = ref('')
const busSubCSampleId = ref('')
const optionParameterClassify = ref([])
const paramFieldApiRet = ref([])
const fieldGroup = ref([])
const collaHeights = ref([])
// 计算属性
const confirmWeightDisabled = computed(() => {
if (currentSample.value.sampleCode && currentAuncel.weightStable === 1 && Number(currentAuncel.weightData) > 0) {
return false
}
return true
})
const checkCupNumReadonly = computed(() => {
if (!currentAuncel.value || currentAuncel.value.id === '') return true
return false
})
const userInfo = computed(() => nx.$store('user').userInfo)
// 方法定义
const getDomHeight = () => {
collaHeights.value = []
nextTick(() => {
if (fieldGroup.value.length) {
fieldGroup.value.forEach((item, index) => {
nx.getRect('#elId' + index).then(res => {
collaHeights.value.push(res.height)
})
})
}
})
}
//返回上一页
const customBack = () => {
uni.redirectTo({
url: '/pages/analysis/sample/sample-work-list'
})
}
const navRightClick = () => {
let url = '/pages/analysis/sample/sample-work-edit-task'
url += '?currentTaskNo=' + currentTaskNo.value
uni.navigateTo({
url: url
})
}
const parameterClassifyChange = (v, a) => {
const groupIndex = ref(0)
curParameterKey.value = v
optionParameterClassify.value.forEach((item, index) => {
if (item.value === v) {
groupIndex.value = index
curParameterTitle.value = item.label
}
})
if (v === '') groupIndex.value = 0
if (groupIndex.value > 0) groupIndex.value--
//自动选中字段
selectedFieldIndex.value = groupIndex.value + '-'
autoNextField()
}
const fieldClick = (field, key) => {
try {
myKeyboard.value.clearNum()
} catch (e) {
console.error(e)
}
if (field.fillingWay === 4) return
selectedField.value = field
selectedFieldIndex.value = key
//判断小数位数
let dataType = field.dataType
if (dataType == null || dataType < -1) dataType = -1
numKeyboardParam.decimal = dataType
if (field.fillingWay === 2) {
listenDeviceData()
} else {
closeDeviceListener()
}
}
//自动切换到下一个字段
const autoNextField = () => {
//selectedFieldIndex是groupIndex-fieldIndex
let groupIndex = 0
let fieldIndex = 0
if (typeof selectedFieldIndex.value === 'undefined' || selectedFieldIndex.value === '')
selectedFieldIndex.value = '0-'
const indexV = selectedFieldIndex.value.split('-')
groupIndex = number(indexV[0])
fieldIndex = indexV[1] === '' ? -1 : number(indexV[1])
const group = fieldGroup.value[groupIndex]
const fields = group.fields
if (fields.length > fieldIndex + 1) {
//如果下一个字段是计算,不自动切换
const fillingWay = fields[fieldIndex + 1].fillingWay || ''
if (fillingWay === '3') return
//切换到下一个字段
const key = groupIndex + '-' + (fieldIndex + 1)
fieldClick(fields[fieldIndex + 1], key)
}
}
//自动切换到下一个样品
const autoNextSample = () => {
//仅在顺序称重时自动切换
// if (!orderWeighChecked.value)
// return;
if (leftList.value.length <= current.value + 1) return
const index = current.value + 1
switchSample(index, true)
}
//手动切换样品
const switchSample = async (index, autoFlag) => {
//重置天平归0
weightDataIsToZero.value = false
//清空数字键盘数据
myKeyboard.value.clearNum()
if (index === current.value) return
// if (orderWeighChecked.value && !autoFlag) return;//顺序称重不允许选中
current.value = index
// 如果为0意味着尚未初始化
if (menuHeight.value === 0 || menuItemHeight.value === 0) {
// await getElRect('menu-scroll-view', 'menuHeight');
await getElRect('u-tab-item', 'menuItemHeight')
}
// 将菜单菜单活动item垂直居中
scrollTop.value = index * menuItemHeight.value + menuItemHeight.value / 2 - menuHeight.value / 2 - 50
//修改当前选中的selectedFieldIndex
if (typeof selectedFieldIndex.value !== 'undefined' && selectedFieldIndex.value.indexOf('-') > 0) {
selectedFieldIndex.value = selectedFieldIndex.value.split('-')[0] + '-'
}
currentSample.value = leftList.value[index]
//读取样品明细字段
getDetailFieldsAndStatus(true)
}
// 获取一个目标元素的高度
const getElRect = (elClass, dataVal) => {
return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this)
query
.select('.' + elClass)
.fields({ size: true }, res => {
// 如果节点尚未生成res值为null循环调用执行
if (!res) {
setTimeout(() => {
getElRect(elClass)
}, 10)
return
}
this[dataVal] = res.height
})
.exec()
})
}
//读取样品明细字段
const getDetailFieldsAndStatus = autoSelectNextField => {
const taskDetailId = currentSample.value.id
//读取回收率配置
loadConRecoveryList()
//查询字段
nx.$api.assayTask.queryFieldsByTaskDetail({ taskDetailId: taskDetailId }).then(res => {
fieldGroup.value = []
if (!res.success) {
nx.$helper.showToast({
title: res.message
})
return
}
//处理“填写&计算”字段:如果后台存了值,则不自动计算
const groupList = res.result
for (const g of groupList) {
g.open = true
for (const f of g.fields) {
if (f.fillingWay === '1' && f.value != null && f.value !== '') {
f.valueTypeManual = '1'
}
}
}
fieldGroup.value = groupList
const arr = [
{
label: '全部',
value: ''
}
]
for (const g of fieldGroup.value) {
const title = g.title
const o = {
label: title,
value: title
}
arr.push(o)
}
// 计算样品详情高度
getDomHeight()
optionParameterClassify.value = arr //字段分类
conAssayTaskId.value = res.additionalProperties.conAssayTaskId
busSubCSampleId.value = res.additionalProperties.busSubCSampleId
const detail = res.additionalProperties.taskDetail
//处理硫值、硫量:未保存过的数据,读取接口返回的硫值、硫量
loadSValue(detail)
//自动生成杯号
autoGenerateCupNum()
//自动选择下一个字段
if (autoSelectNextField) autoNextField()
//按公式计算值,并检查原数据与计算后的数据是否一致
calAndCheck()
})
}
const checkLoadSValue = () => {
const vKey = 'sRange'
const vF = getFieldByKey(vKey)
if (vF === null) return false
const v = vF.value
if (v === null || v === '') return true
try {
if (v === 0 || number(v) === 0) return true
} catch (e) {}
return false
}
//读取硫值、硫量
const loadSValue = detail => {
let flag = checkLoadSValue()
if (!flag) return
const vKey = 'sValue'
const rKey = 'sRange'
const vF = getFieldByKey(vKey)
if (vF !== null) {
vF.value = detail.svalue
}
const rF = getFieldByKey(rKey)
if (rF !== null) {
rF.value = detail.srange
}
}
const getFieldByKey = key => {
const group = fieldGroup.value
let field = null
for (let g of group) {
for (let f of g.fields) {
const dicKey = f.dicKey
if (dicKey && dicKey === key) {
field = f
break
}
}
}
return field
}
//按公式计算值,并检查原数据与计算后的数据是否一致
const calAndCheck = () => {
//提取原本数值
const oldVal = fitUpValues()
calcAnalysisValue(fieldGroup.value)
if (checkDataNew(oldVal)) return
const newVal = fitUpValues(oldVal)
//检查是否一致
let modifyFlag = false
for (const o in oldVal) {
let old = oldVal[o]
if (old === null) old = 0
//判断是否number
try {
if (number(old) !== number(newVal[o])) {
modifyFlag = true
}
} catch (e) {
if (old !== newVal[o]) modifyFlag = true
}
}
if (modifyFlag)
uni.showToast({
title: '数据有修改,请检查所有样品,并重新保存!',
duration: 2000,
icon: 'error'
})
}
//检查是否是新数据
const checkDataNew = data => {
for (const o in data) {
if (data[o] !== null && data[o] !== '0') return false
}
return true
}
//组装表单值,用于数据比对
const fitUpValues = () => {
const val = {}
for (const g of fieldGroup.value) {
for (const f of g.fields) {
const detailId = f.detailId
if (!detailId) continue
val[detailId] = f.value
}
}
return val
}
const clearFieldVal = () => {
uni.showModal({
title: '提示',
content: '清空当前字段的值,是否继续?',
cancelColor: '#0055A2',
confirmColor: '#0055A2',
success: res => {
if (res.cancel) {
return
}
selectedField.value.value = ''
selectedField.value.valueTypeManual = '0'
//重新计算
calcAnalysisValue(fieldGroup.value)
}
})
}
//自动填写筛上杯号
const autoFillBhUp = val => {
const bh = Number(val) + 1
for (const g of fieldGroup.value) {
for (const f of g.fields) {
if (f.dicKey && f.dicKey === 'bh_up') {
f.value = bh + ''
return
}
}
}
}
const checkBh = () => {
//检查杯号
let hasHb = true
for (const g of fieldGroup.value) {
for (const f of g.fields) {
if (f.dicKey && f.dicKey.indexOf('bh_') >= 0) {
if (typeof f.value === 'undefined' || f.value === null || f.value === '') hasHb = false
}
}
}
if (!hasHb) {
nx.$helper.showToast({
title: '杯号不能为空,请填写杯号!'
})
return false
}
return true
}
const saveAuncelData = () => {
//检查杯号
if (!checkBh()) return
//保存数据
if (!weightDataIsToZero.value) {
nx.$helper.showToast({
title: '天平必须先回零!'
})
return
}
//获取当前重量
let weight = currentAuncel.weightData
selectedField.value.value = weight
const curFieldDicKey = selectedField.value.dicKey
let upDownFlag = ''
if (curFieldDicKey && curFieldDicKey !== '' && curFieldDicKey.indexOf('_') > 0) {
upDownFlag = curFieldDicKey.substring(curFieldDicKey.lastIndexOf('_') + 1)
}
//这里不修改weightDataIsToZero在切换样品是修改
// weightDataIsToZero.value = false;
//保存天平编号、天平名称、称重时间。支持筛上、筛下
if (curFieldDicKey && curFieldDicKey.indexOf('sampleWeight') >= 0) {
let auncelNoFieldKey = 'auncelNo'
let measureTimeFieldKey = 'measureTime'
if (upDownFlag && upDownFlag !== '') {
auncelNoFieldKey += '_' + upDownFlag
measureTimeFieldKey += '_' + upDownFlag
}
const auncelNoField = getFieldByKey(auncelNoFieldKey)
if (auncelNoField !== null) {
auncelNoField.value = currentAuncel.code
}
const measureTimeField = getFieldByKey(measureTimeFieldKey)
if (measureTimeField !== null) {
measureTimeField.value = nx.$dayjs.format('yyyy-MM-dd hh:mm:ss')
}
}
//指派单明细上的称重时间
curSample.value.measureTime = nx.$dayjs.format('yyyy-MM-dd hh:mm:ss')
//计算
try {
calcAnalysisValue(fieldGroup.value)
} catch (e) {
console.error(e)
}
//自动跳转下一个字段
setTimeout(() => {
autoNextField()
}, 100)
}
const checkSubmit = () => {
//检查必填字段
let fieldNames = ''
for (const g of fieldGroup.value) {
for (const f of g.fields) {
let name = f.shortName
if (name === null || name === '') name = f.name
if (f.allowNull === 1 || f.allowNull === '1') continue
if (f.value === null || f.value === '') {
fieldNames += name + ''
}
}
}
if (fieldNames !== '') {
nx.$helper.showToast({
title: '请填写必填字段:' + fieldNames
})
return false
}
//检查不能为0的字段
fieldNames = ''
for (const g of fieldGroup.value) {
for (const f of g.fields) {
let name = f.shortName
if (name === null || name === '') name = f.name
//检查不可为0的数据
if (f.notZero === 1 || f.notZero === '1') {
if (f.value === null || f.value === '' || number(f.value) === 0) {
fieldNames += name + ''
}
}
}
}
if (fieldNames !== '') {
nx.$helper.showToast({
title: '这几个值不能为0请检查' + fieldNames
})
return false
}
return true
}
const saveDetail = () => {
//检查杯号
if (!checkBh()) return
const valueList = []
let cupNum = 0
let detailId = currentSample.value.id
for (const g of fieldGroup.value) {
for (const f of g.fields) {
if (f.dicKey === 'bh' || f.dicKey === 'bh_up') cupNum = f.value
valueList.push({
id: f.detailId,
type: f.pOrE,
value: f.value,
name: f.name,
dataType: f.dataType
})
}
}
const data = {
busSubCSampleId: busSubCSampleId.value,
conAssayTaskId: conAssayTaskId.value,
measureTime: curSample.value.measureTime,
elementParamValueList: valueList,
busAssayTaskDetailId: detailId
}
if (typeof cupNum !== 'undefined' && cupNum !== null && cupNum !== '' && cupNum !== 0 && cupNum !== '0') {
//提交杯号,保存到后台
data.cupNum = cupNum
//标记当前杯号
markCurCupNum(cupNum)
}
nx.$api.assayTask.saveDetailValue(data).then(res => {
//如果仅保存,在这里触发切换
setTimeout(() => {
autoNextSample()
}, 1000)
})
}
//提交任务指派单
const submitTask = () => {
const data = {
taskNo: currentTaskNo.value
}
const msg = '请确认所有样品数据都已保存,然后再提交指派单!是否继续?'
uni.showModal({
title: '提示',
content: msg,
cancelColor: '#0055A2',
confirmColor: '#0055A2',
success: res => {
if (res.cancel) return
nx.$api.assayTask.submitTask(data).then(res => {
uni.navigateTo({
url: '/pages/analysis/sample/sample-report'
})
})
}
})
}
//自动生成杯号(仅顺序称重):第一杯:手填,后续杯 = 上一杯 + 1
const autoGenerateCupNum = () => {
if (!orderGenBH.value) return
let cupNum = 0
let current = current.value
if (typeof current === 'undefined' || current === null) current = 0
//第一杯
if (current === 0) {
cupNum = 1
putCupNum(cupNum)
return
}
//取上一个样品的杯号
const sample = leftList.value[current - 1]
cupNum = sample.cupNum
if (typeof cupNum === 'undefined' || cupNum === null || cupNum === '') return
//杯号赋值到当前样品
putCupNum(Number(cupNum) + 1, current, Number(cupNum))
}
const markCurCupNum = cupNum => {
currentSample.value.cupNum = cupNum
}
const putCupNum = (cupNum, sampleIndex, lastCupNum) => {
if (typeof sampleIndex === 'undefined' || sampleIndex === null) sampleIndex = 0
const cupNumKey = 'bh'
for (const fields of fieldGroup.value) {
if (typeof fields === 'undefined') continue
//杯号
for (const field of fields.fields) {
if (field.dicKey === cupNumKey) {
if (typeof field.value === 'undefined' || field.value === null || field.value === '') {
field.value = cupNum
return true
}
}
}
}
//处理筛上筛下
if (lastCupNum && lastCupNum === sampleIndex) {
//上一个杯号 == 默认杯号
cupNum = sampleIndex * 2 + 1
}
for (const fields of fieldGroup.value) {
if (typeof fields === 'undefined') continue
//杯号-筛下
for (const field of fields.fields) {
if (field.dicKey === cupNumKey + '_down') {
if (typeof field.value === 'undefined' || field.value === null || field.value === '') {
field.value = cupNum
cupNum++
break
}
}
}
//杯号-筛上
for (const field of fields.fields) {
if (field.dicKey === cupNumKey + '_up') {
if (typeof field.value === 'undefined' || field.value === null || field.value === '') {
field.value = cupNum
return true
}
}
}
}
return false
}
const loadConRecoveryList = () => {
const conBaseSampleId = currentSample.value.conBaseSampleId
const rParam = {
pageNo: 1,
pageSize: -1,
conBaseSampleId: conBaseSampleId
}
const storageKey = 'ConRecoveryRateList'
nx.$api.assayTask
.queryConRecoveryRateList(rParam)
.then(res => {
const result = res.result
if (typeof result === 'undefined' || result === null || !result) {
uni.setStorage(storageKey, [])
return
}
const records = result.records
if (!records || records.length === 0) {
uni.setStorage(storageKey, [])
return
}
uni.setStorageSync(storageKey, records)
})
.catch(err => {
console.error(err)
})
}
const getAssayTaskDetail = taskNo => {
leftList.value = []
nx.$api.assayTask
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
.then(res => {
let dataList = res.result
dataList.forEach((item, index) => {
leftList.value.push(item)
})
if (leftList.value.length === 0) {
return
}
current.value = 0
currentSample.value = leftList.value[0]
getDetailFieldsAndStatus(true)
})
.catch(err => {
console.error(err)
})
}
const apiRequest = async url => {
return request({
url: url,
method: 'GET'
})
}
//读取字段里的API选项
const loadFieldApiData = async fieldGroups => {
for (const fields of fieldGroups) {
for (const field of fields.fields) {
const api = field.api
const type = field.type
if (type === 'select' && api && api !== '') {
//读取API选项
try {
const res = await apiRequest(api)
const data = res
field.options = data
field.rangeKey = 'displayName'
} catch (e) {}
}
}
}
}
/*
* 选择器组件确认事件
* event 事件默认参数
* field 当前字段
* 处理逻辑:
* field.value = 选中的值
* field.valueText = 选中的文本
* */
const dicPickerConfirm = (event, field) => {
const checked = event.value[0]
const displayName = checked.displayName
const value = checked.dictValue
const extField1 = checked.extField1
field.value = value
field.valueText = value
const relatedFieldNo = '8'
//查找并处理关联字段
for (const fieldGroup of fieldGroup.value) {
for (const field of fieldGroup.fields) {
if (field.paramNo === relatedFieldNo) {
field.value = extField1
break
}
}
}
//触发计算公式
calcAnalysisValue(fieldGroup.value)
}
// 天平监听
const listenDeviceData = () => {
//监听websocket返回来的数据
//设备数据
uni.$on('deviceData', res => {
switch (res.deviceType) {
case 'balance':
if (currentAuncel.id === res.deviceId) {
currentAuncel.weightData = res.weightData
currentAuncel.weightUnit = res.weightUnit
currentAuncel.weightStable = res.weightStable
currentAuncel.isConnected = 1
if (Number(res.weightData) === 0) {
weightDataIsToZero.value = true
}
}
break
default:
break
}
})
//设备状态
uni.$on('deviceStatus', res => {
if (currentAuncel.id === res.deviceId) {
if (res.connected === 0) {
currentAuncel.weightStable = 0
currentAuncel.weightData = '天平断开'
currentAuncel.weightUnit = ''
}
}
currentAuncel.isConnected = res.connected
})
//控制设备状态
uni.$on('controlDevice', res => {
if (currentAuncel.id === res.deviceId) {
currentAuncel.id = ''
currentAuncel.name = ''
currentAuncel.code = ''
currentAuncel.weightStable = 0
currentAuncel.weightData = '请选天平'
currentAuncel.weightUnit = ''
}
})
//连接断开
uni.$on('connClose', res => {
//重置
currentAuncel.weightData = ''
currentAuncel.weightUnit = ''
currentAuncel.weightStable = 0
currentAuncel.controlUserName = ''
currentAuncel.isConnected = 0
})
}
const closeDeviceListener = () => {
uni.$off('deviceData')
uni.$off('deviceStatus')
uni.$off('controlDevice')
uni.$off('connClose')
}
const loadDevice = () => {
//注册websocket
let regData = {
msgId: nx.$helper.uuid(),
cmd: 'register',
clientType: 'caaClient',
data: {
userId: userInfo.value.id,
tenantId: getTenantId(),
userRealName: userInfo.value.realname
}
}
nx.$measure.setRegData(JSON.stringify(regData))
nx.$measure.reOpen()
}
// 键盘监听
const listenNumKeyboard = () => {
//键盘监听
uni.$on('keyboardOK', res => {
if (res === null) {
clearFieldVal()
return
}
//自动补全小数位数
const dataType = selectedField.value.dataType || 0
let val = res.val
//判断val小数位如果小于设定位数则补全
if (dataType > 0) {
if (val === '') val = '0'
if (typeof val === 'number') val += ''
let dotLen = 0
if (val.indexOf('.') > 0) dotLen = val.length - val.indexOf('.') - 1
else val += '.'
while (dotLen < dataType) {
dotLen++
val += '0'
}
}
selectedField.value.value = val
selectedField.value.valueTypeManual = '1' //标记为手动值,不参与计算
const dicKey = selectedField.value.dicKey
if (dicKey && dicKey === 'bh_down') {
autoFillBhUp(val)
}
calcAnalysisValue(fieldGroup.value)
//自动跳转下一个字段
setTimeout(() => {
autoNextField()
}, 60)
})
}
const closeNumKeyBoardListener = () => {
uni.$off('keyboardOK')
}
const closeDeviceLink = () => {
const deviceId = currentAuncel.id
releaseDeviceControl(deviceId)
}
//释放设备控制
const releaseDeviceControl = deviceId => {
if (!deviceId || deviceId === '') return
let controlDevice = {
msgId: deviceId,
cmd: 'controlDevice',
clientType: 'caaClient',
data: {
deviceId: deviceId,
isControl: false,
controlRealName: userInfo.value.nickname
}
}
//发送控制数据
nx.$measure.send(JSON.stringify(controlDevice))
}
const getDataSourceTypeShow = val => {
if (val === 2) return '【筛上】'
if (val === 3) return '【筛下】'
return ''
}
// 天平选择
const selectAuncel = () => {
showAuncelSelector.value = true
}
// 天平选择回调
const auncelDoSelect = res => {
const data = res.data
if (data) {
currentAuncel.id = data.deviceId
currentAuncel.name = data.deviceName
currentAuncel.code = data.deviceCode
}
}
// refs
const myKeyboard = ref(null)
const auncelSelector = ref(null)
// 生命周期
const { lockOrientation } = useScreenOrientation()
onLoad(param => {
lockOrientation('landscape')
if (param.currentTaskNo) {
currentTaskNo.value = param.currentTaskNo
currentTaskType.value = param.currentTaskType
}
title.value = '样品分析-任务指派单:' + param.currentTaskNo
getAssayTaskDetail(currentTaskNo.value)
loadFieldApiData(fieldGroup.value)
listenNumKeyboard()
})
onShow(() => {
loadDevice()
listenDeviceData()
})
onHide(() => {
closeDeviceListener()
})
onUnload(() => {
closeDeviceLink()
closeDeviceListener()
closeNumKeyBoardListener()
})
onBackPress(() => {
customBack()
return true
})
</script>
<style lang="scss" scoped>
/* 原始样式保留不变 */
.navbar-right {
font-size: 16px;
color: #fff;
display: flex;
}
.content-title-name {
font-size: 18px;
font-weight: 300;
padding: 4px 0;
display: flex;
justify-content: center;
}
.current-sample-code {
font-weight: bold;
margin-right: 20px;
}
.form-item-my {
display: flex;
align-items: center;
margin-bottom: 10px;
height: 35px;
font-size: 13px;
}
.selected-field {
background-color: #bada55;
}
.label-my {
flex: 5;
}
.label-high-light {
color: #000;
font-weight: 600;
font-size: 1.2em;
}
.field-high-light {
color: #000;
font-weight: 600;
font-size: 1.2em;
}
.label-my sub {
font-size: 0.6em;
vertical-align: sub;
}
.content-my {
flex: 2;
text-align: right;
.select-my {
color: #303133;
font-size: 15px;
padding: 6px 8px;
border: 1px solid #dcdcdc;
border-radius: 5px;
}
}
.content-my-text-value {
color: rgb(48, 49, 51);
}
.content-my-text-placeholder {
color: #c0c4cc;
}
.content-left-scroll {
height: 60vh;
}
.content-right-scroll {
height: 68vh;
}
.u-tab-item {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 16px;
color: #444;
font-weight: 400;
padding: 4px 0;
border-bottom: 2px dotted #444;
}
.u-tab-item-active {
color: #0055a2;
font-weight: 600;
}
.btn-operation {
width: 80%;
font-size: 18px;
margin-top: 10px;
}
.auncel {
width: 90%;
height: 55vh;
background-image: url(/static/images/auncel.png);
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
display: flex;
flex-direction: column;
}
.auncel-title {
flex: 3;
display: flex;
justify-content: center;
align-items: center;
font-size: 32px;
}
.auncel-weight {
flex: 2.5;
display: flex;
align-items: center;
padding-bottom: 20px;
box-sizing: border-box;
position: relative;
}
.weight {
font-size: 42px;
width: 100%;
padding: 0 60px;
}
.weight-data {
color: #4cd964;
font-family: zzjc-lcd;
}
.weight-data-yellow {
color: #ffff00;
font-family: zzjc-lcd;
}
.weight-data-warning {
color: #ff3333;
font-family: zzjc-lcd;
}
.weight-unit {
position: absolute;
right: 30px;
color: #ffffff;
font-size: 32px;
}
@media (max-width: 700px) {
.auncel {
height: 45vh;
}
.auncel-title {
font-size: 20px;
}
.auncel-weight {
padding-bottom: 15px;
}
.weight {
font-size: 26px;
}
.weight-unit {
font-size: 20px;
}
}
.field-name {
font-size: 26px;
padding: 8px;
}
.my-collapse {
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 10px 5px 0;
.title {
font-weight: bold;
}
}
.content {
overflow: hidden;
padding-right: 10px;
transition: height 100ms;
}
@media (max-width: 700px) {
.content-left-scroll {
height: 45vh;
}
.content-right-scroll {
height: 55vh;
}
.field-name {
font-size: 16px;
}
}
</style>